@brandsync/wc 1.7.0 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (314) hide show
  1. package/custom-elements.json +201 -13
  2. package/dist/brandsync-wc/brandsync-wc.esm.js +1 -1
  3. package/dist/brandsync-wc/{p-387ed85f.entry.js → p-002d258d.entry.js} +1 -1
  4. package/dist/brandsync-wc/{p-819a3dae.entry.js → p-03b08508.entry.js} +1 -1
  5. package/dist/brandsync-wc/{p-fde5e9f1.entry.js → p-051d3064.entry.js} +1 -1
  6. package/dist/brandsync-wc/{p-f243683f.entry.js → p-05c7de28.entry.js} +1 -1
  7. package/dist/brandsync-wc/{p-2d6955d6.entry.js → p-062cfa34.entry.js} +1 -1
  8. package/dist/brandsync-wc/p-0e39b9cc.entry.js +1 -0
  9. package/dist/brandsync-wc/{p-538cc08e.entry.js → p-114d2fd8.entry.js} +1 -1
  10. package/dist/brandsync-wc/{p-70e035ed.entry.js → p-153da845.entry.js} +1 -1
  11. package/dist/brandsync-wc/{p-6754dfa9.entry.js → p-1c0db4d6.entry.js} +1 -1
  12. package/dist/brandsync-wc/{p-4a1a5d6e.entry.js → p-1f7208cb.entry.js} +1 -1
  13. package/dist/brandsync-wc/{p-d06543a5.entry.js → p-235ae012.entry.js} +1 -1
  14. package/dist/brandsync-wc/{p-2700a63e.entry.js → p-275072dd.entry.js} +1 -1
  15. package/dist/brandsync-wc/p-2891d40b.entry.js +1 -0
  16. package/dist/brandsync-wc/p-29027559.entry.js +1 -0
  17. package/dist/brandsync-wc/p-2e7a1082.entry.js +1 -0
  18. package/dist/brandsync-wc/{p-a36a0e6a.entry.js → p-3176d1d3.entry.js} +1 -1
  19. package/dist/brandsync-wc/{p-0d86c22d.entry.js → p-33e44094.entry.js} +1 -1
  20. package/dist/brandsync-wc/{p-e427bbda.entry.js → p-360452c2.entry.js} +1 -1
  21. package/dist/brandsync-wc/p-3ec74d35.entry.js +1 -0
  22. package/dist/brandsync-wc/p-4574a707.entry.js +1 -0
  23. package/dist/brandsync-wc/p-4e5dcaff.entry.js +1 -0
  24. package/dist/brandsync-wc/p-612cf79c.entry.js +1 -0
  25. package/dist/brandsync-wc/{p-61db6ce2.entry.js → p-61c1a2a3.entry.js} +1 -1
  26. package/dist/brandsync-wc/{p-85c70f6d.entry.js → p-6331de9c.entry.js} +1 -1
  27. package/dist/brandsync-wc/{p-d03eba2b.entry.js → p-652d4068.entry.js} +1 -1
  28. package/dist/brandsync-wc/{p-9c1be2a0.entry.js → p-6bf2f116.entry.js} +1 -1
  29. package/dist/brandsync-wc/{p-c3a28a89.entry.js → p-70f64b25.entry.js} +1 -1
  30. package/dist/brandsync-wc/{p-06e9b45d.entry.js → p-7b00dd36.entry.js} +1 -1
  31. package/dist/brandsync-wc/{p-13e4966c.entry.js → p-7c94e6ae.entry.js} +1 -1
  32. package/dist/brandsync-wc/p-835a8dc7.entry.js +1 -0
  33. package/dist/brandsync-wc/{p-77433277.entry.js → p-8ee0153b.entry.js} +1 -1
  34. package/dist/brandsync-wc/{p-e1069ba9.entry.js → p-9f65f998.entry.js} +1 -1
  35. package/dist/brandsync-wc/{p-b2dc9886.entry.js → p-9f8852a0.entry.js} +1 -1
  36. package/dist/brandsync-wc/{p-DuyFft7h.js → p-AgoOSYvW.js} +2 -2
  37. package/dist/brandsync-wc/{p-fba01c96.entry.js → p-a09e157f.entry.js} +1 -1
  38. package/dist/brandsync-wc/{p-55e6438c.entry.js → p-a0d5a620.entry.js} +1 -1
  39. package/dist/brandsync-wc/{p-8b2ab522.entry.js → p-a5feaad4.entry.js} +1 -1
  40. package/dist/brandsync-wc/{p-cbaa19ab.entry.js → p-a92eec48.entry.js} +1 -1
  41. package/dist/brandsync-wc/{p-3eab22a5.entry.js → p-ae978c9a.entry.js} +1 -1
  42. package/dist/brandsync-wc/{p-f2ce9a1f.entry.js → p-bbcc47b0.entry.js} +1 -1
  43. package/dist/brandsync-wc/{p-30d6a6d0.entry.js → p-bc69a96c.entry.js} +1 -1
  44. package/dist/brandsync-wc/{p-80cf96ff.entry.js → p-be2c1740.entry.js} +1 -1
  45. package/dist/brandsync-wc/p-bf53ad52.entry.js +1 -0
  46. package/dist/brandsync-wc/p-c10d2529.entry.js +1 -0
  47. package/dist/brandsync-wc/{p-8b57f606.entry.js → p-c4de155c.entry.js} +1 -1
  48. package/dist/brandsync-wc/p-c7f4c114.entry.js +1 -0
  49. package/dist/brandsync-wc/{p-c197cc95.entry.js → p-cfcf3f0a.entry.js} +1 -1
  50. package/dist/brandsync-wc/{p-f8f03334.entry.js → p-de7c946d.entry.js} +1 -1
  51. package/dist/brandsync-wc/{p-600b5434.entry.js → p-e9b43754.entry.js} +1 -1
  52. package/dist/brandsync-wc/{p-d25b7b74.entry.js → p-ee7da23d.entry.js} +1 -1
  53. package/dist/brandsync-wc/{p-4bff9b4b.entry.js → p-f006d962.entry.js} +1 -1
  54. package/dist/brandsync-wc/p-f074f94e.entry.js +1 -0
  55. package/dist/brandsync-wc/{p-7ed5670e.entry.js → p-f7d47c17.entry.js} +1 -1
  56. package/dist/brandsync-wc/{p-f518f486.entry.js → p-fc58c663.entry.js} +1 -1
  57. package/dist/brandsync-wc/{p-be14f99f.entry.js → p-fddc7bd1.entry.js} +1 -1
  58. package/dist/cjs/brandsync-wc.cjs.js +2 -2
  59. package/dist/cjs/bs-accordion.cjs.entry.js +2 -2
  60. package/dist/cjs/bs-ai-disclaimer.cjs.entry.js +1 -1
  61. package/dist/cjs/bs-ai-greeting.cjs.entry.js +1 -1
  62. package/dist/cjs/bs-ai-thinking.cjs.entry.js +1 -1
  63. package/dist/cjs/bs-attachment-list.cjs.entry.js +2 -2
  64. package/dist/cjs/bs-attachment.cjs.entry.js +2 -2
  65. package/dist/cjs/bs-avatar.cjs.entry.js +1 -1
  66. package/dist/cjs/bs-badge.cjs.entry.js +1 -1
  67. package/dist/cjs/bs-breadcrumb-overflow.cjs.entry.js +2 -2
  68. package/dist/cjs/bs-breadcrumb.cjs.entry.js +1 -1
  69. package/dist/cjs/bs-breadcrumbs.cjs.entry.js +1 -1
  70. package/dist/cjs/bs-button-skeleton.cjs.entry.js +1 -1
  71. package/dist/cjs/bs-button.cjs.entry.js +23 -4
  72. package/dist/cjs/bs-card.cjs.entry.js +2 -2
  73. package/dist/cjs/bs-carousel.cjs.entry.js +1 -1
  74. package/dist/cjs/bs-chatbot-feedback.cjs.entry.js +2 -2
  75. package/dist/cjs/bs-chatbot-header.cjs.entry.js +1 -1
  76. package/dist/cjs/bs-chatbot-response-action.cjs.entry.js +2 -2
  77. package/dist/cjs/bs-chatbot-sources-drawer.cjs.entry.js +1 -1
  78. package/dist/cjs/bs-chatbot-suggestion-button.cjs.entry.js +1 -1
  79. package/dist/cjs/bs-checkbox-skeleton.cjs.entry.js +1 -1
  80. package/dist/cjs/bs-checkbox.cjs.entry.js +48 -3
  81. package/dist/cjs/bs-chip-filter.cjs.entry.js +2 -2
  82. package/dist/cjs/bs-chip-informative.cjs.entry.js +2 -2
  83. package/dist/cjs/bs-chip-input.cjs.entry.js +2 -2
  84. package/dist/cjs/bs-composer-status-banner.cjs.entry.js +1 -1
  85. package/dist/cjs/bs-composer.cjs.entry.js +1 -1
  86. package/dist/cjs/bs-data-table.cjs.entry.js +2 -2
  87. package/dist/cjs/bs-dialog.cjs.entry.js +2 -2
  88. package/dist/cjs/bs-file-upload-item.cjs.entry.js +1 -1
  89. package/dist/cjs/bs-file-upload.cjs.entry.js +1 -1
  90. package/dist/cjs/bs-icon-button.cjs.entry.js +22 -3
  91. package/dist/cjs/bs-inline-tab.cjs.entry.js +7 -3
  92. package/dist/cjs/bs-input.cjs.entry.js +69 -5
  93. package/dist/cjs/bs-logo.cjs.entry.js +1 -1
  94. package/dist/cjs/bs-menu-item.cjs.entry.js +1 -1
  95. package/dist/cjs/bs-menu.cjs.entry.js +25 -3
  96. package/dist/cjs/bs-navigation-drawer-item.cjs.entry.js +2 -2
  97. package/dist/cjs/bs-navigation-drawer.cjs.entry.js +2 -2
  98. package/dist/cjs/bs-navigation-header.cjs.entry.js +1 -1
  99. package/dist/cjs/bs-pagination.cjs.entry.js +1 -1
  100. package/dist/cjs/bs-progress-linear.cjs.entry.js +1 -1
  101. package/dist/cjs/bs-progress.cjs.entry.js +8 -3
  102. package/dist/cjs/bs-radio.cjs.entry.js +81 -12
  103. package/dist/cjs/bs-slider.cjs.entry.js +54 -2
  104. package/dist/cjs/bs-snackbar.cjs.entry.js +1 -1
  105. package/dist/cjs/bs-source-link.cjs.entry.js +2 -2
  106. package/dist/cjs/bs-stepper-step.cjs.entry.js +2 -2
  107. package/dist/cjs/bs-stepper.cjs.entry.js +3 -2
  108. package/dist/cjs/bs-switch.cjs.entry.js +36 -3
  109. package/dist/cjs/bs-tab.cjs.entry.js +7 -3
  110. package/dist/cjs/bs-tabs.cjs.entry.js +51 -2
  111. package/dist/cjs/bs-toast.cjs.entry.js +1 -1
  112. package/dist/cjs/bs-tooltip.cjs.entry.js +2 -2
  113. package/dist/cjs/{index-D7e6DfLV.js → index-kk9YXF2S.js} +43 -5
  114. package/dist/cjs/loader.cjs.js +2 -2
  115. package/dist/collection/components/bs-a11y.cmp.test.js +54 -0
  116. package/dist/collection/components/bs-accordion/bs-accordion.css +5 -5
  117. package/dist/collection/components/bs-attachment/bs-attachment.css +1 -1
  118. package/dist/collection/components/bs-attachment-list/bs-attachment-list.css +1 -1
  119. package/dist/collection/components/bs-breadcrumb/bs-breadcrumb-overflow/bs-breadcrumb-overflow.css +1 -1
  120. package/dist/collection/components/bs-button/bs-button/bs-button.css +17 -10
  121. package/dist/collection/components/bs-button/bs-button/bs-button.js +16 -3
  122. package/dist/collection/components/bs-button/bs-icon-button/bs-icon-button.js +16 -3
  123. package/dist/collection/components/bs-card/bs-card.css +4 -0
  124. package/dist/collection/components/bs-chatbot-feedback/bs-chatbot-feedback.css +1 -1
  125. package/dist/collection/components/bs-chatbot-response-action/bs-chatbot-response-action.css +3 -3
  126. package/dist/collection/components/bs-checkbox/bs-checkbox.css +8 -1
  127. package/dist/collection/components/bs-checkbox/bs-checkbox.js +100 -2
  128. package/dist/collection/components/bs-chip/bs-chip-filter/bs-chip-filter.css +4 -4
  129. package/dist/collection/components/bs-chip/bs-chip-informative/bs-chip-informative.css +4 -4
  130. package/dist/collection/components/bs-chip/bs-chip-input/bs-chip-input.css +5 -5
  131. package/dist/collection/components/bs-data-table/bs-data-table.css +1 -1
  132. package/dist/collection/components/bs-dialog/bs-dialog.css +5 -1
  133. package/dist/collection/components/bs-form-association.cmp.test.js +172 -0
  134. package/dist/collection/components/bs-input/bs-input.css +10 -4
  135. package/dist/collection/components/bs-input/bs-input.js +82 -4
  136. package/dist/collection/components/bs-keyboard-nav.cmp.test.js +100 -0
  137. package/dist/collection/components/bs-menu/bs-menu.css +4 -0
  138. package/dist/collection/components/bs-menu/bs-menu.js +33 -1
  139. package/dist/collection/components/bs-progress/bs-progress.js +27 -2
  140. package/dist/collection/components/bs-radio/bs-radio.cmp.test.js +4 -10
  141. package/dist/collection/components/bs-radio/bs-radio.css +7 -1
  142. package/dist/collection/components/bs-radio/bs-radio.js +92 -14
  143. package/dist/collection/components/bs-radio/bs-radio.stories.js +1 -10
  144. package/dist/collection/components/bs-slider/bs-slider.js +68 -2
  145. package/dist/collection/components/bs-source-link/bs-source-link.css +2 -2
  146. package/dist/collection/components/bs-stepper/bs-stepper/bs-stepper.js +3 -2
  147. package/dist/collection/components/bs-stepper/bs-stepper-step/bs-stepper-step.css +3 -3
  148. package/dist/collection/components/bs-switch/bs-switch.css +10 -1
  149. package/dist/collection/components/bs-switch/bs-switch.js +69 -2
  150. package/dist/collection/components/bs-tab/bs-inline-tab/bs-inline-tab.css +2 -2
  151. package/dist/collection/components/bs-tab/bs-inline-tab/bs-inline-tab.js +24 -1
  152. package/dist/collection/components/bs-tab/bs-tab/bs-tab.css +2 -2
  153. package/dist/collection/components/bs-tab/bs-tab/bs-tab.js +24 -1
  154. package/dist/collection/components/bs-tab/bs-tabs/bs-tabs.js +57 -1
  155. package/dist/collection/components/bs-tooltip/bs-tooltip.js +1 -1
  156. package/dist/collection/components/forms/forms.stories.js +63 -0
  157. package/dist/collection/components/ui-shell/bs-navigation-drawer/bs-navigation-drawer/bs-navigation-drawer.js +1 -1
  158. package/dist/collection/components/ui-shell/bs-navigation-drawer/bs-navigation-drawer-item/bs-navigation-drawer-item.js +1 -1
  159. package/dist/components/bs-accordion.js +1 -1
  160. package/dist/components/bs-ai-disclaimer.js +1 -1
  161. package/dist/components/bs-ai-greeting.js +1 -1
  162. package/dist/components/bs-ai-thinking.js +1 -1
  163. package/dist/components/bs-attachment-list.js +1 -1
  164. package/dist/components/bs-attachment.js +1 -1
  165. package/dist/components/bs-avatar.js +1 -1
  166. package/dist/components/bs-badge.js +1 -1
  167. package/dist/components/bs-breadcrumb-overflow.js +1 -1
  168. package/dist/components/bs-breadcrumb.js +1 -1
  169. package/dist/components/bs-breadcrumbs.js +1 -1
  170. package/dist/components/bs-button-skeleton.js +1 -1
  171. package/dist/components/bs-button.js +1 -1
  172. package/dist/components/bs-card.js +1 -1
  173. package/dist/components/bs-carousel.js +1 -1
  174. package/dist/components/bs-chatbot-feedback.js +1 -1
  175. package/dist/components/bs-chatbot-header.js +1 -1
  176. package/dist/components/bs-chatbot-response-action.js +1 -1
  177. package/dist/components/bs-chatbot-sources-drawer.js +1 -1
  178. package/dist/components/bs-chatbot-suggestion-button.js +1 -1
  179. package/dist/components/bs-checkbox-skeleton.js +1 -1
  180. package/dist/components/bs-checkbox.js +1 -1
  181. package/dist/components/bs-chip-filter.js +1 -1
  182. package/dist/components/bs-chip-informative.js +1 -1
  183. package/dist/components/bs-chip-input.js +1 -1
  184. package/dist/components/bs-composer-status-banner.js +1 -1
  185. package/dist/components/bs-composer.js +1 -1
  186. package/dist/components/bs-data-table.js +1 -1
  187. package/dist/components/bs-dialog.js +1 -1
  188. package/dist/components/bs-file-upload-item.js +1 -1
  189. package/dist/components/bs-file-upload.js +1 -1
  190. package/dist/components/bs-icon-button.js +1 -1
  191. package/dist/components/bs-inline-tab.js +1 -1
  192. package/dist/components/bs-input.js +1 -1
  193. package/dist/components/bs-logo.js +1 -1
  194. package/dist/components/bs-menu-item.js +1 -1
  195. package/dist/components/bs-menu.js +1 -1
  196. package/dist/components/bs-navigation-drawer-item.js +1 -1
  197. package/dist/components/bs-navigation-drawer.js +1 -1
  198. package/dist/components/bs-navigation-header.js +1 -1
  199. package/dist/components/bs-pagination.js +1 -1
  200. package/dist/components/bs-progress-linear.js +1 -1
  201. package/dist/components/bs-progress.js +1 -1
  202. package/dist/components/bs-radio.js +1 -1
  203. package/dist/components/bs-slider.js +1 -1
  204. package/dist/components/bs-snackbar.js +1 -1
  205. package/dist/components/bs-source-link.js +1 -1
  206. package/dist/components/bs-stepper-step.js +1 -1
  207. package/dist/components/bs-stepper.js +1 -1
  208. package/dist/components/bs-switch.js +1 -1
  209. package/dist/components/bs-tab.js +1 -1
  210. package/dist/components/bs-tabs.js +1 -1
  211. package/dist/components/bs-toast.js +1 -1
  212. package/dist/components/bs-tooltip.js +1 -1
  213. package/dist/components/index.js +1 -1
  214. package/dist/components/{p-LYvoWZt4.js → p-BA3c9Rom.js} +1 -1
  215. package/dist/components/{p-Dqt-Qp4W.js → p-BfEyD6Ku.js} +1 -1
  216. package/dist/components/p-BkHYJIyj.js +1 -0
  217. package/dist/components/p-Buly0d5G.js +1 -0
  218. package/dist/components/p-CpUHnsq9.js +1 -0
  219. package/dist/components/{p-BxFJ74fI.js → p-EtXc94lJ.js} +1 -1
  220. package/dist/components/{p-mnGJPH85.js → p-JYlmcxBn.js} +1 -1
  221. package/dist/components/{p-BhvV55-6.js → p-R2MlkUzD.js} +1 -1
  222. package/dist/components/{p-Dx8gAOVC.js → p-wei0Ija2.js} +1 -1
  223. package/dist/esm/brandsync-wc.js +3 -3
  224. package/dist/esm/bs-accordion.entry.js +2 -2
  225. package/dist/esm/bs-ai-disclaimer.entry.js +1 -1
  226. package/dist/esm/bs-ai-greeting.entry.js +1 -1
  227. package/dist/esm/bs-ai-thinking.entry.js +1 -1
  228. package/dist/esm/bs-attachment-list.entry.js +2 -2
  229. package/dist/esm/bs-attachment.entry.js +2 -2
  230. package/dist/esm/bs-avatar.entry.js +1 -1
  231. package/dist/esm/bs-badge.entry.js +1 -1
  232. package/dist/esm/bs-breadcrumb-overflow.entry.js +2 -2
  233. package/dist/esm/bs-breadcrumb.entry.js +1 -1
  234. package/dist/esm/bs-breadcrumbs.entry.js +1 -1
  235. package/dist/esm/bs-button-skeleton.entry.js +1 -1
  236. package/dist/esm/bs-button.entry.js +23 -4
  237. package/dist/esm/bs-card.entry.js +2 -2
  238. package/dist/esm/bs-carousel.entry.js +1 -1
  239. package/dist/esm/bs-chatbot-feedback.entry.js +2 -2
  240. package/dist/esm/bs-chatbot-header.entry.js +1 -1
  241. package/dist/esm/bs-chatbot-response-action.entry.js +2 -2
  242. package/dist/esm/bs-chatbot-sources-drawer.entry.js +1 -1
  243. package/dist/esm/bs-chatbot-suggestion-button.entry.js +1 -1
  244. package/dist/esm/bs-checkbox-skeleton.entry.js +1 -1
  245. package/dist/esm/bs-checkbox.entry.js +48 -3
  246. package/dist/esm/bs-chip-filter.entry.js +2 -2
  247. package/dist/esm/bs-chip-informative.entry.js +2 -2
  248. package/dist/esm/bs-chip-input.entry.js +2 -2
  249. package/dist/esm/bs-composer-status-banner.entry.js +1 -1
  250. package/dist/esm/bs-composer.entry.js +1 -1
  251. package/dist/esm/bs-data-table.entry.js +2 -2
  252. package/dist/esm/bs-dialog.entry.js +2 -2
  253. package/dist/esm/bs-file-upload-item.entry.js +1 -1
  254. package/dist/esm/bs-file-upload.entry.js +1 -1
  255. package/dist/esm/bs-icon-button.entry.js +22 -3
  256. package/dist/esm/bs-inline-tab.entry.js +7 -3
  257. package/dist/esm/bs-input.entry.js +69 -5
  258. package/dist/esm/bs-logo.entry.js +1 -1
  259. package/dist/esm/bs-menu-item.entry.js +1 -1
  260. package/dist/esm/bs-menu.entry.js +25 -3
  261. package/dist/esm/bs-navigation-drawer-item.entry.js +2 -2
  262. package/dist/esm/bs-navigation-drawer.entry.js +2 -2
  263. package/dist/esm/bs-navigation-header.entry.js +1 -1
  264. package/dist/esm/bs-pagination.entry.js +1 -1
  265. package/dist/esm/bs-progress-linear.entry.js +1 -1
  266. package/dist/esm/bs-progress.entry.js +8 -3
  267. package/dist/esm/bs-radio.entry.js +81 -12
  268. package/dist/esm/bs-slider.entry.js +54 -2
  269. package/dist/esm/bs-snackbar.entry.js +1 -1
  270. package/dist/esm/bs-source-link.entry.js +2 -2
  271. package/dist/esm/bs-stepper-step.entry.js +2 -2
  272. package/dist/esm/bs-stepper.entry.js +3 -2
  273. package/dist/esm/bs-switch.entry.js +36 -3
  274. package/dist/esm/bs-tab.entry.js +7 -3
  275. package/dist/esm/bs-tabs.entry.js +51 -2
  276. package/dist/esm/bs-toast.entry.js +1 -1
  277. package/dist/esm/bs-tooltip.entry.js +2 -2
  278. package/dist/esm/{index-DuyFft7h.js → index-AgoOSYvW.js} +43 -5
  279. package/dist/esm/loader.js +3 -3
  280. package/dist/types/components/bs-a11y.cmp.test.d.ts +1 -0
  281. package/dist/types/components/bs-button/bs-button/bs-button.d.ts +4 -1
  282. package/dist/types/components/bs-button/bs-icon-button/bs-icon-button.d.ts +4 -1
  283. package/dist/types/components/bs-checkbox/bs-checkbox.d.ts +12 -0
  284. package/dist/types/components/bs-form-association.cmp.test.d.ts +1 -0
  285. package/dist/types/components/bs-input/bs-input.d.ts +12 -0
  286. package/dist/types/components/bs-keyboard-nav.cmp.test.d.ts +1 -0
  287. package/dist/types/components/bs-menu/bs-menu.d.ts +4 -0
  288. package/dist/types/components/bs-progress/bs-progress.d.ts +5 -0
  289. package/dist/types/components/bs-radio/bs-radio.d.ts +23 -9
  290. package/dist/types/components/bs-slider/bs-slider.d.ts +9 -0
  291. package/dist/types/components/bs-switch/bs-switch.d.ts +10 -0
  292. package/dist/types/components/bs-tab/bs-inline-tab/bs-inline-tab.d.ts +4 -0
  293. package/dist/types/components/bs-tab/bs-tab/bs-tab.d.ts +4 -0
  294. package/dist/types/components/bs-tab/bs-tabs/bs-tabs.d.ts +7 -0
  295. package/dist/types/components/forms/forms.stories.d.ts +6 -0
  296. package/dist/types/components.d.ts +141 -7
  297. package/package.json +1 -1
  298. package/readme.md +14 -0
  299. package/dist/brandsync-wc/p-18021c05.entry.js +0 -1
  300. package/dist/brandsync-wc/p-1c8160c2.entry.js +0 -1
  301. package/dist/brandsync-wc/p-21c14465.entry.js +0 -1
  302. package/dist/brandsync-wc/p-27100489.entry.js +0 -1
  303. package/dist/brandsync-wc/p-309f920e.entry.js +0 -1
  304. package/dist/brandsync-wc/p-4b0938a5.entry.js +0 -1
  305. package/dist/brandsync-wc/p-5312b6a5.entry.js +0 -1
  306. package/dist/brandsync-wc/p-777a9dbf.entry.js +0 -1
  307. package/dist/brandsync-wc/p-a96856fd.entry.js +0 -1
  308. package/dist/brandsync-wc/p-b0634051.entry.js +0 -1
  309. package/dist/brandsync-wc/p-bcee3d60.entry.js +0 -1
  310. package/dist/brandsync-wc/p-e61efe4d.entry.js +0 -1
  311. package/dist/brandsync-wc/p-f14bda01.entry.js +0 -1
  312. package/dist/components/p-BaxFLlKS.js +0 -1
  313. package/dist/components/p-DI5GgSqL.js +0 -1
  314. package/dist/components/p-v4T0qPVn.js +0 -1
@@ -49,6 +49,10 @@ import { h } from "@stencil/core";
49
49
  // comments (see stencil.js's parseStyleDocs/parseCssComment), not the class-level JSDoc.
50
50
  export class BsInput {
51
51
  el;
52
+ internals;
53
+ initialValue = '';
54
+ /** The `name` the value is submitted under in a surrounding `<form>`'s `FormData`. */
55
+ name;
52
56
  /** Current value. Native `input`/`change` events don't cross the Shadow DOM boundary, so this
53
57
  * component re-dispatches them as `bsInput`/`bsChange` custom events instead. Mutable so the
54
58
  * `number` type's stepper buttons can update it directly. */
@@ -124,9 +128,44 @@ export class BsInput {
124
128
  * children, before first render -- see `bs-button`'s `componentWillLoad` for why `slotchange`
125
129
  * alone isn't enough for a slot that starts (and stays) empty. */
126
130
  componentWillLoad() {
131
+ this.initialValue = this.value;
132
+ this.syncFormValue();
127
133
  this.hasIcon = this.hasSlottedContent('icon');
128
134
  this.hasEndIcon = this.hasSlottedContent('end-icon');
129
135
  }
136
+ onFormStateChange() {
137
+ this.syncFormValue();
138
+ }
139
+ /** Pushes the current value + validity to the surrounding form. `chip` submits one entry per
140
+ * chip under `name`; every other type submits `value` as a single string. */
141
+ syncFormValue() {
142
+ if (!this.internals?.setFormValue)
143
+ return;
144
+ if (this.type === 'chip' && this.name) {
145
+ const data = new FormData();
146
+ this.chips.forEach(c => data.append(this.name, c));
147
+ this.internals.setFormValue(data);
148
+ }
149
+ else {
150
+ this.internals.setFormValue(this.value ?? '');
151
+ }
152
+ const empty = this.type === 'chip' ? this.chips.length === 0 : !this.value;
153
+ if (this.required && empty) {
154
+ this.internals.setValidity({ valueMissing: true }, 'Please fill out this field.');
155
+ }
156
+ else {
157
+ this.internals.setValidity({});
158
+ }
159
+ }
160
+ /** Called by the browser when the surrounding `<form>` is reset. */
161
+ formResetCallback() {
162
+ this.value = this.initialValue;
163
+ }
164
+ /** Called when the browser restores form state (back/forward navigation, autofill). */
165
+ formStateRestoreCallback(state) {
166
+ if (typeof state === 'string')
167
+ this.value = state;
168
+ }
130
169
  hasSlottedContent(slotName) {
131
170
  return Array.from(this.el.childNodes).some(node => node.nodeType === Node.ELEMENT_NODE && node.getAttribute('slot') === slotName);
132
171
  }
@@ -138,10 +177,12 @@ export class BsInput {
138
177
  };
139
178
  onInput = (ev) => {
140
179
  const value = ev.target.value;
180
+ this.value = value;
141
181
  this.bsInput.emit(value);
142
182
  };
143
183
  onChange = (ev) => {
144
184
  const value = ev.target.value;
185
+ this.value = value;
145
186
  this.bsChange.emit(value);
146
187
  };
147
188
  clampNumber(n) {
@@ -236,7 +277,8 @@ export class BsInput {
236
277
  if (value && index < this.length - 1) {
237
278
  this.pinInputs[index + 1]?.focus();
238
279
  }
239
- this.bsChange.emit(this.pinInputs.map(i => i?.value ?? '').join(''));
280
+ this.value = this.pinInputs.map(i => i?.value ?? '').join('');
281
+ this.bsChange.emit(this.value);
240
282
  };
241
283
  onPinKeydown = (index) => (ev) => {
242
284
  if (ev.key === 'Backspace' && !ev.target.value && index > 0) {
@@ -341,14 +383,15 @@ export class BsInput {
341
383
  return (h("div", { part: "field", class: `bs-input__field bs-input__field--pin ${errorClass} ${disabledClass}` }, Array.from({ length: this.length }).map((_, i) => (h("input", { id: i === 0 ? 'control' : undefined, ref: el => (this.pinInputs[i] = el), part: "control", class: "bs-input__pin-cell", type: "text", inputMode: "numeric", maxLength: 1, value: this.value[i] ?? '', disabled: this.disabled, "aria-required": this.required ? 'true' : undefined, onInput: this.onPinInput(i), onKeyDown: this.onPinKeydown(i) })))));
342
384
  }
343
385
  render() {
344
- return (h("div", { key: 'd65ab3191cd18c77385ffbb8b827e6161dafceaf', class: `bs-input ${this.error ? 'bs-input--error' : ''}` }, this.label && (
386
+ return (h("div", { key: '1149cfe8eaa6085007a8d9a963b6c909bb6cda13', class: `bs-input ${this.error ? 'bs-input--error' : ''}` }, this.label && (
345
387
  // "control" is a fixed id, not a generated one -- safe because Shadow DOM scopes ids
346
388
  // to this instance's own shadow root, so it can never collide with another bs-input.
347
- h("label", { key: '762c10bb8afcd1f0609ba75b0d6d8162c483b5b5', part: "label", class: "bs-input__label", htmlFor: "control" }, this.label, this.required && (h("span", { key: '9a74743816c07b81b4db39616cee80ef69d60ca1', part: "required", class: "bs-input__required", "aria-hidden": "true" }, "*")))), this.type === 'pin' ? this.renderPinField() : this.type === 'initials' ? this.renderInitialsField() : this.renderField(), this.error ? (h("span", { id: "description-or-error", part: "error", class: "bs-input__error" }, h("svg", { part: "error-icon", class: "bs-input__error-icon", viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true" }, h("circle", { cx: "8", cy: "8", r: "6.5", stroke: "currentColor", "stroke-width": "1.2" }), h("path", { d: "M8 5V8.5", stroke: "currentColor", "stroke-width": "1.2", "stroke-linecap": "round" }), h("circle", { cx: "8", cy: "11", r: "0.75", fill: "currentColor" })), this.error)) : this.description ? (h("span", { id: "description-or-error", part: "description", class: "bs-input__description" }, this.description)) : null));
389
+ h("label", { key: 'd737018c1e1ebcde529710d8ea58c6bed76f2abf', part: "label", class: "bs-input__label", htmlFor: "control" }, this.label, this.required && (h("span", { key: '5993b19cbfcb8725550c1292c00795c8463e554c', part: "required", class: "bs-input__required", "aria-hidden": "true" }, "*")))), this.type === 'pin' ? this.renderPinField() : this.type === 'initials' ? this.renderInitialsField() : this.renderField(), this.error ? (h("span", { id: "description-or-error", part: "error", class: "bs-input__error" }, h("svg", { part: "error-icon", class: "bs-input__error-icon", viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true" }, h("circle", { cx: "8", cy: "8", r: "6.5", stroke: "currentColor", "stroke-width": "1.2" }), h("path", { d: "M8 5V8.5", stroke: "currentColor", "stroke-width": "1.2", "stroke-linecap": "round" }), h("circle", { cx: "8", cy: "11", r: "0.75", fill: "currentColor" })), this.error)) : this.description ? (h("span", { id: "description-or-error", part: "description", class: "bs-input__description" }, this.description)) : null));
348
390
  }
349
391
  static get is() { return "bs-input"; }
350
392
  static get encapsulation() { return "shadow"; }
351
393
  static get delegatesFocus() { return true; }
394
+ static get formAssociated() { return true; }
352
395
  static get originalStyleUrls() {
353
396
  return {
354
397
  "$": ["bs-input.css"]
@@ -361,6 +404,25 @@ export class BsInput {
361
404
  }
362
405
  static get properties() {
363
406
  return {
407
+ "name": {
408
+ "type": "string",
409
+ "mutable": false,
410
+ "complexType": {
411
+ "original": "string",
412
+ "resolved": "string",
413
+ "references": {}
414
+ },
415
+ "required": false,
416
+ "optional": true,
417
+ "docs": {
418
+ "tags": [],
419
+ "text": "The `name` the value is submitted under in a surrounding `<form>`'s `FormData`."
420
+ },
421
+ "getter": false,
422
+ "setter": false,
423
+ "reflect": true,
424
+ "attribute": "name"
425
+ },
364
426
  "value": {
365
427
  "type": "string",
366
428
  "mutable": true,
@@ -493,7 +555,7 @@ export class BsInput {
493
555
  },
494
556
  "getter": false,
495
557
  "setter": false,
496
- "reflect": false,
558
+ "reflect": true,
497
559
  "attribute": "disabled",
498
560
  "defaultValue": "false"
499
561
  },
@@ -866,6 +928,21 @@ export class BsInput {
866
928
  }];
867
929
  }
868
930
  static get elementRef() { return "el"; }
931
+ static get watchers() {
932
+ return [{
933
+ "propName": "value",
934
+ "methodName": "onFormStateChange"
935
+ }, {
936
+ "propName": "type",
937
+ "methodName": "onFormStateChange"
938
+ }, {
939
+ "propName": "chips",
940
+ "methodName": "onFormStateChange"
941
+ }, {
942
+ "propName": "required",
943
+ "methodName": "onFormStateChange"
944
+ }];
945
+ }
869
946
  static get listeners() {
870
947
  return [{
871
948
  "name": "keydown",
@@ -881,4 +958,5 @@ export class BsInput {
881
958
  "passive": false
882
959
  }];
883
960
  }
961
+ static get attachInternalsMemberName() { return "internals"; }
884
962
  }
@@ -0,0 +1,100 @@
1
+ import { render, h, describe, it, expect } from "@stencil/vitest";
2
+ // Cross-component coverage for WAI-ARIA keyboard patterns (tabs, menu) and slider form submission.
3
+ const key = (el, k) => el.dispatchEvent(new KeyboardEvent('keydown', { key: k, bubbles: true, composed: true, cancelable: true }));
4
+ const btn = (el) => el.shadowRoot.querySelector('button');
5
+ describe('bs-tabs keyboard', () => {
6
+ const tabs = (orientation = 'horizontal') => (h("bs-tabs", { orientation: orientation }, h("bs-tab", { selected: true }, "One"), h("bs-tab", null, "Two"), h("bs-tab", { disabled: true }, "Three"), h("bs-tab", null, "Four")));
7
+ it('roving tabindex: only the selected tab is tabbable', async () => {
8
+ const { root, waitForChanges } = await render(tabs());
9
+ await waitForChanges();
10
+ const [a, b] = Array.from(root.querySelectorAll('bs-tab'));
11
+ expect(btn(a).tabIndex).toBe(0);
12
+ expect(btn(b).tabIndex).toBe(-1);
13
+ });
14
+ it('ArrowRight selects the next enabled tab (skipping disabled) and wraps', async () => {
15
+ const { root, waitForChanges } = await render(tabs());
16
+ const els = Array.from(root.querySelectorAll('bs-tab'));
17
+ key(btn(els[0]), 'ArrowRight');
18
+ await waitForChanges();
19
+ expect(els[1].selected).toBe(true);
20
+ key(btn(els[1]), 'ArrowRight');
21
+ await waitForChanges();
22
+ expect(els[3].selected).toBe(true);
23
+ key(btn(els[3]), 'ArrowRight');
24
+ await waitForChanges();
25
+ expect(els[0].selected).toBe(true);
26
+ expect(btn(els[0]).tabIndex).toBe(0);
27
+ expect(btn(els[3]).tabIndex).toBe(-1);
28
+ });
29
+ it('Home/End jump to first/last enabled tab', async () => {
30
+ const { root, waitForChanges } = await render(tabs());
31
+ const els = Array.from(root.querySelectorAll('bs-tab'));
32
+ key(btn(els[0]), 'End');
33
+ await waitForChanges();
34
+ expect(els[3].selected).toBe(true);
35
+ key(btn(els[3]), 'Home');
36
+ await waitForChanges();
37
+ expect(els[0].selected).toBe(true);
38
+ });
39
+ it('vertical orientation uses Up/Down and ignores Left/Right', async () => {
40
+ const { root, waitForChanges } = await render(tabs('vertical'));
41
+ const els = Array.from(root.querySelectorAll('bs-tab'));
42
+ key(btn(els[0]), 'ArrowRight');
43
+ await waitForChanges();
44
+ expect(els[0].selected).toBe(true);
45
+ key(btn(els[0]), 'ArrowDown');
46
+ await waitForChanges();
47
+ expect(els[1].selected).toBe(true);
48
+ });
49
+ it('with nothing selected, the first enabled tab is the tab stop', async () => {
50
+ const { root, waitForChanges } = await render(h("bs-tabs", null, h("bs-tab", { disabled: true }, "A"), h("bs-tab", null, "B"), h("bs-tab", null, "C")));
51
+ await waitForChanges();
52
+ const els = Array.from(root.querySelectorAll('bs-tab'));
53
+ expect(btn(els[1]).tabIndex).toBe(0);
54
+ expect(btn(els[2]).tabIndex).toBe(-1);
55
+ });
56
+ });
57
+ describe('bs-menu keyboard', () => {
58
+ const menu = () => (h("bs-menu", null, h("bs-menu-item", null, "Edit"), h("bs-menu-item", { disabled: true }, "Copy"), h("bs-menu-item", null, "Delete")));
59
+ it('ArrowDown/ArrowUp move focus between enabled items, wrapping', async () => {
60
+ const { root, waitForChanges } = await render(menu());
61
+ const [a, , c] = Array.from(root.querySelectorAll('bs-menu-item'));
62
+ btn(a).focus();
63
+ key(btn(a), 'ArrowDown');
64
+ await waitForChanges();
65
+ expect(c.shadowRoot.activeElement).toBe(btn(c));
66
+ key(btn(c), 'ArrowDown');
67
+ expect(a.shadowRoot.activeElement).toBe(btn(a));
68
+ key(btn(a), 'ArrowUp');
69
+ expect(c.shadowRoot.activeElement).toBe(btn(c));
70
+ });
71
+ it('Home/End jump to first/last enabled item', async () => {
72
+ const { root } = await render(menu());
73
+ const [a, , c] = Array.from(root.querySelectorAll('bs-menu-item'));
74
+ btn(a).focus();
75
+ key(btn(a), 'End');
76
+ expect(c.shadowRoot.activeElement).toBe(btn(c));
77
+ key(btn(c), 'Home');
78
+ expect(a.shadowRoot.activeElement).toBe(btn(a));
79
+ });
80
+ });
81
+ describe('bs-slider form association', () => {
82
+ it('submits its value under name and resets with the form', async () => {
83
+ const { root, waitForChanges } = await render(h("form", null, h("bs-slider", { name: "vol", value: 30 })));
84
+ const form = root;
85
+ await waitForChanges();
86
+ expect(new FormData(form).get('vol')).toBe('30');
87
+ const slider = form.querySelector('bs-slider');
88
+ slider.value = 70;
89
+ await waitForChanges();
90
+ expect(new FormData(form).get('vol')).toBe('70');
91
+ form.reset();
92
+ await waitForChanges();
93
+ expect(new FormData(form).get('vol')).toBe('30');
94
+ });
95
+ it('range type submits start then end under the same name', async () => {
96
+ const { root, waitForChanges } = await render(h("form", null, h("bs-slider", { name: "price", type: "range", valueStart: 10, valueEnd: 90 })));
97
+ await waitForChanges();
98
+ expect(new FormData(root).getAll('price')).toEqual(['10', '90']);
99
+ });
100
+ });
@@ -18,3 +18,7 @@
18
18
  box-shadow: var(--bs-menu-shadow);
19
19
  font-family: inherit;
20
20
  }
21
+
22
+ @media (forced-colors: active) {
23
+ .bs-menu { border: 1px solid CanvasText; }
24
+ }
@@ -21,8 +21,30 @@ import { h } from "@stencil/core";
21
21
  * @prop --bs-menu-shadow - Elevation shadow of the menu box. Aliased to `--bs-shadow-sm`.
22
22
  */
23
23
  export class BsMenu {
24
+ el;
25
+ /** WAI-ARIA menu pattern: Up/Down move focus between enabled items (wrapping), Home/End jump
26
+ * to the first/last. Items are the slotted `bs-menu-item` children. */
27
+ onKeyDown(ev) {
28
+ if (!['ArrowDown', 'ArrowUp', 'Home', 'End'].includes(ev.key))
29
+ return;
30
+ const items = Array.from(this.el.querySelectorAll('bs-menu-item')).filter(i => !i.disabled);
31
+ if (items.length === 0)
32
+ return;
33
+ const current = items.findIndex(i => i === ev.target || i.contains(ev.target));
34
+ ev.preventDefault();
35
+ let next;
36
+ if (ev.key === 'Home')
37
+ next = 0;
38
+ else if (ev.key === 'End')
39
+ next = items.length - 1;
40
+ else if (ev.key === 'ArrowDown')
41
+ next = current === -1 ? 0 : (current + 1) % items.length;
42
+ else
43
+ next = current === -1 ? items.length - 1 : (current - 1 + items.length) % items.length;
44
+ items[next].shadowRoot?.querySelector('button')?.focus();
45
+ }
24
46
  render() {
25
- return (h("div", { key: '6d24ccbadf6c68bba52b6737e91c4d131fbce331', part: "list", class: "bs-menu", role: "menu" }, h("slot", { key: '94cc6884049a7836b3438a2fc112f0a2c7a1473a' })));
47
+ return (h("div", { key: '4f2995d0f53f85656de8595fb6a9909833c6369d', part: "list", class: "bs-menu", role: "menu" }, h("slot", { key: '0824f2a784eb4cfa28a2868f19c056570b56ba63' })));
26
48
  }
27
49
  static get is() { return "bs-menu"; }
28
50
  static get encapsulation() { return "shadow"; }
@@ -36,4 +58,14 @@ export class BsMenu {
36
58
  "$": ["bs-menu.css"]
37
59
  };
38
60
  }
61
+ static get elementRef() { return "el"; }
62
+ static get listeners() {
63
+ return [{
64
+ "name": "keydown",
65
+ "method": "onKeyDown",
66
+ "target": undefined,
67
+ "capture": false,
68
+ "passive": false
69
+ }];
70
+ }
39
71
  }
@@ -45,6 +45,11 @@ export class BsProgress {
45
45
  * `indeterminate` never shows a label regardless of this prop, since there's no percentage to
46
46
  * show. Defaults to `true`. */
47
47
  showLabel = true;
48
+ /** Accessible name for the progress bar (e.g. "Uploading report"). Required for assistive tech --
49
+ * a `role="progressbar"` with no name is an axe-core `aria-progressbar-name` violation. Setting
50
+ * `aria-label` on the host doesn't reach the inner element across the shadow boundary, so this
51
+ * prop binds it directly -- same pattern as `bs-button`'s `ariaLabel`. */
52
+ ariaLabel = null;
48
53
  get clampedValue() {
49
54
  return Math.min(100, Math.max(0, this.value));
50
55
  }
@@ -64,7 +69,7 @@ export class BsProgress {
64
69
  const isDeterminate = this.type === 'determinate';
65
70
  const value = this.clampedValue;
66
71
  const dashOffset = circumference * (1 - value / 100);
67
- return (h("div", { key: '0534b118c5954b821dc3e9f1bb0b4a0ac746633a', part: "container", class: "bs-progress" }, h("div", { key: 'fd20dfde6fe2de74c0622589294a066f526fe213', class: "bs-progress__box", style: { width: `${diameter}px`, height: `${diameter}px` }, role: "progressbar", "aria-valuemin": isDeterminate ? '0' : undefined, "aria-valuemax": isDeterminate ? '100' : undefined, "aria-valuenow": isDeterminate ? String(value) : undefined, "aria-busy": isDeterminate ? undefined : 'true' }, isDeterminate ? (h("svg", { class: "bs-progress__ring", width: diameter, height: diameter, viewBox: `0 0 ${diameter} ${diameter}` }, h("circle", { part: "track", class: "bs-progress__track", cx: diameter / 2, cy: diameter / 2, r: radius, "stroke-width": this.strokeWidth, fill: "none" }), h("circle", { part: "arc", class: "bs-progress__arc", cx: diameter / 2, cy: diameter / 2, r: radius, "stroke-width": this.strokeWidth, fill: "none", "stroke-linecap": "round", "stroke-dasharray": circumference, "stroke-dashoffset": dashOffset }))) : (
72
+ return (h("div", { key: 'add8dfafdbee4eaa9712c6e2ba6ddff6f8f6c47f', part: "container", class: "bs-progress" }, h("div", { key: 'fd89c73aa247fc3770187a45dc4681857e257d4e', class: "bs-progress__box", style: { width: `${diameter}px`, height: `${diameter}px` }, role: "progressbar", "aria-label": this.ariaLabel, "aria-valuemin": isDeterminate ? '0' : undefined, "aria-valuemax": isDeterminate ? '100' : undefined, "aria-valuenow": isDeterminate ? String(value) : undefined, "aria-busy": isDeterminate ? undefined : 'true' }, isDeterminate ? (h("svg", { class: "bs-progress__ring", width: diameter, height: diameter, viewBox: `0 0 ${diameter} ${diameter}` }, h("circle", { part: "track", class: "bs-progress__track", cx: diameter / 2, cy: diameter / 2, r: radius, "stroke-width": this.strokeWidth, fill: "none" }), h("circle", { part: "arc", class: "bs-progress__arc", cx: diameter / 2, cy: diameter / 2, r: radius, "stroke-width": this.strokeWidth, fill: "none", "stroke-linecap": "round", "stroke-dasharray": circumference, "stroke-dashoffset": dashOffset }))) : (
68
73
  // Figma's indeterminate ring is a fading "comet tail" (transparent -> solid color),
69
74
  // not a flat-colored partial arc -- SVG has no angular/conic gradient primitive, so a
70
75
  // CSS conic-gradient (color varying by angle) + a radial-gradient mask (carving the
@@ -73,7 +78,7 @@ export class BsProgress {
73
78
  h("div", { part: "arc", class: "bs-progress__spinner", style: {
74
79
  '--bs-progress-spinner-stroke-width': `${this.strokeWidth}px`,
75
80
  '--bs-progress-spinner-sweep': `${INDETERMINATE_SWEEP_DEGREES}deg`,
76
- } }))), isDeterminate && this.showLabel && (h("span", { key: '20b74322a926ff40188de525103f91992d9bc508', part: "label", class: `bs-progress__label bs-progress__label--${this.size}` }, value, "%"))));
81
+ } }))), isDeterminate && this.showLabel && (h("span", { key: '9c86dc195204f28c06e05bf09f823241bf9f3a86', part: "label", class: `bs-progress__label bs-progress__label--${this.size}` }, value, "%"))));
77
82
  }
78
83
  static get is() { return "bs-progress"; }
79
84
  static get encapsulation() { return "shadow"; }
@@ -206,6 +211,26 @@ export class BsProgress {
206
211
  "reflect": false,
207
212
  "attribute": "show-label",
208
213
  "defaultValue": "true"
214
+ },
215
+ "ariaLabel": {
216
+ "type": "string",
217
+ "mutable": false,
218
+ "complexType": {
219
+ "original": "string | null",
220
+ "resolved": "string",
221
+ "references": {}
222
+ },
223
+ "required": false,
224
+ "optional": false,
225
+ "docs": {
226
+ "tags": [],
227
+ "text": "Accessible name for the progress bar (e.g. \"Uploading report\"). Required for assistive tech --\na `role=\"progressbar\"` with no name is an axe-core `aria-progressbar-name` violation. Setting\n`aria-label` on the host doesn't reach the inner element across the shadow boundary, so this\nprop binds it directly -- same pattern as `bs-button`'s `ariaLabel`."
228
+ },
229
+ "getter": false,
230
+ "setter": false,
231
+ "reflect": false,
232
+ "attribute": "aria-label",
233
+ "defaultValue": "null"
209
234
  }
210
235
  };
211
236
  }
@@ -30,14 +30,9 @@ describe('bs-radio', () => {
30
30
  expect(input).toEqualAttribute('name', 'fruit');
31
31
  expect(input).toEqualAttribute('value', 'apple');
32
32
  });
33
- // NOTE: each <bs-radio> has its own shadow root, so its native <input> lives in its own separate
34
- // DOM tree. Per the HTML spec, a "radio button group" is scoped to a single tree -- shadow DOM
35
- // boundaries mean same-`name` inputs in *different* <bs-radio> instances are NOT part of the same
36
- // native radio group, so clicking one does not natively uncheck another. This is a real, tested
37
- // limitation of composing native <input type="radio"> inside separate shadow roots, not a bug in
38
- // this component -- see bs-radio.mdx's Accessibility section for the documented gap. Within a
39
- // single instance's own shadow root the native input still behaves like a normal radio input.
40
- it('same-name bs-radio instances do NOT natively uncheck each other, since each lives in its own shadow root', async () => {
33
+ // Native same-name grouping can't cross each <bs-radio>'s own shadow root, so the component
34
+ // implements mutual exclusion itself (see bs-radio.tsx's `name` prop doc).
35
+ it('same-name bs-radio instances uncheck each other', async () => {
41
36
  const { root, waitForChanges } = await render(h("div", null, h("bs-radio", { name: "fruit", value: "apple" }), h("bs-radio", { name: "fruit", value: "banana" })));
42
37
  const [first, second] = Array.from(root.querySelectorAll('bs-radio'));
43
38
  const firstInput = first.shadowRoot.querySelector('input');
@@ -48,8 +43,7 @@ describe('bs-radio', () => {
48
43
  secondInput.click();
49
44
  await waitForChanges();
50
45
  expect(secondInput.checked).toBe(true);
51
- // Real (shadow-DOM-scoped) native behavior: the first input is untouched, not unchecked.
52
- expect(firstInput.checked).toBe(true);
46
+ expect(firstInput.checked).toBe(false);
53
47
  });
54
48
  describe('disabled', () => {
55
49
  it('sets the disabled attribute on the native input', async () => {
@@ -34,7 +34,7 @@
34
34
  .bs-radio__input {
35
35
  position: absolute;
36
36
  top: 0;
37
- left: 0;
37
+ inset-inline-start: 0;
38
38
  width: 100%;
39
39
  height: 100%;
40
40
  margin: 0;
@@ -162,3 +162,9 @@
162
162
  .bs-radio--disabled .bs-radio__label {
163
163
  color: var(--bs-radio-label-disabled);
164
164
  }
165
+
166
+ /* Windows High Contrast: ring and dot in system colors. */
167
+ @media (forced-colors: active) {
168
+ .bs-radio__circle { border: 2px solid ButtonText; forced-color-adjust: none; background: Canvas; }
169
+ .bs-radio__dot { background: Highlight; }
170
+ }
@@ -22,13 +22,10 @@ import { h } from "@stencil/core";
22
22
  export class BsRadio {
23
23
  el;
24
24
  /** The native radio input's `name` attribute. Passed straight through to the native input inside.
25
- * NOTE: because each `bs-radio` renders its own shadow root, native `<input type="radio">`
26
- * mutual-exclusion grouping (which the HTML spec scopes to a single DOM tree) does NOT extend
27
- * across separate `bs-radio` instances sharing the same `name` -- checking one will not natively
28
- * uncheck another. Within a single instance this still behaves like a normal radio input. See
29
- * bs-radio.mdx's Accessibility section for this known gap; consumers building a group currently
30
- * need to manage mutual exclusivity themselves (e.g. via `bsChange`) until a `bs-radio-group`
31
- * wrapper exists. */
25
+ * Also the group key: `bs-radio` elements sharing a `name` within the same root (document or
26
+ * shadow root) behave as one radio group -- checking one unchecks the others, arrow keys move
27
+ * between them, and only one is in the tab order. Native grouping can't cross each radio's own
28
+ * shadow root, so this component implements it itself. */
32
29
  name;
33
30
  /** The native radio input's `value` attribute -- read from `event.target.value`, or used when
34
31
  * wiring the group up to a form. */
@@ -40,9 +37,65 @@ export class BsRadio {
40
37
  disabled = false;
41
38
  /** Emitted with this radio's own `value` when it becomes checked via user interaction. Since
42
39
  * native same-`name` grouping doesn't cross shadow-root boundaries (see the `name` prop's doc),
43
- * a consumer wiring up a group of `bs-radio`s must itself set `checked = false` on the others in
44
- * response to this event -- there's no automatic un-checking to rely on here. */
40
+ * a consumer wiring up a group of `bs-radio`s does not need to un-check the others itself. */
45
41
  bsChange;
42
+ internals;
43
+ initialChecked = false;
44
+ /** Whether any radio in this group is checked -- drives roving tabindex (an unchecked radio is
45
+ * only tabbable while nothing in its group is checked). */
46
+ groupHasChecked = false;
47
+ /** Other `bs-radio`s sharing this one's `name` within the same root, in DOM order (including
48
+ * this one). */
49
+ groupMembers() {
50
+ if (!this.name)
51
+ return [this.el];
52
+ const root = this.el.getRootNode();
53
+ return Array.from(root.querySelectorAll('bs-radio')).filter(r => r.name === this.name);
54
+ }
55
+ onCheckedChange() {
56
+ this.internals?.setFormValue?.(this.checked ? this.value ?? 'on' : null);
57
+ if (this.checked) {
58
+ this.groupMembers().forEach(r => {
59
+ if (r !== this.el && r.checked)
60
+ r.checked = false;
61
+ });
62
+ }
63
+ this.refreshGroupState();
64
+ }
65
+ onAnyRadioChange() {
66
+ this.refreshGroupState();
67
+ }
68
+ refreshGroupState() {
69
+ this.groupHasChecked = this.groupMembers().some(r => r.checked);
70
+ }
71
+ componentWillLoad() {
72
+ this.initialChecked = this.checked;
73
+ this.internals?.setFormValue?.(this.checked ? this.value ?? 'on' : null);
74
+ }
75
+ componentDidLoad() {
76
+ this.refreshGroupState();
77
+ }
78
+ /** Called by the browser when the surrounding `<form>` is reset. */
79
+ formResetCallback() {
80
+ this.checked = this.initialChecked;
81
+ }
82
+ /** WAI-ARIA radio group pattern: arrows move selection (and focus) to the previous/next enabled
83
+ * radio in the group, wrapping at the ends. */
84
+ onKeyDown = (ev) => {
85
+ const forward = ev.key === 'ArrowRight' || ev.key === 'ArrowDown';
86
+ const backward = ev.key === 'ArrowLeft' || ev.key === 'ArrowUp';
87
+ if (!forward && !backward)
88
+ return;
89
+ const members = this.groupMembers().filter(r => !r.disabled);
90
+ if (members.length < 2)
91
+ return;
92
+ ev.preventDefault();
93
+ const idx = members.indexOf(this.el);
94
+ const next = members[(idx + (forward ? 1 : -1) + members.length) % members.length];
95
+ const input = next.shadowRoot?.querySelector('input');
96
+ input?.click();
97
+ input?.focus();
98
+ };
46
99
  onChange = (ev) => {
47
100
  const checked = ev.target.checked;
48
101
  this.checked = checked;
@@ -51,11 +104,12 @@ export class BsRadio {
51
104
  }
52
105
  };
53
106
  render() {
54
- return (h("label", { key: '9aed6afb95bd1875b02e96aa17ff9419f15f52df', class: `bs-radio ${this.disabled ? 'bs-radio--disabled' : ''}` }, h("input", { key: 'dd8976d2e0c907d2dc2af45540d3c10abca9abb1', class: "bs-radio__input", type: "radio", name: this.name, value: this.value, checked: this.checked, disabled: this.disabled, onChange: this.onChange }), h("span", { key: 'dd68936424c34d8395b1b3540e875a0efb154365', class: "bs-radio__hit-area" }, h("span", { key: 'a5d17ebb7a01851ce3cd0a2acc3c16be911ffc53', class: "bs-radio__state-layer" }, h("span", { key: '46ed4553f672712b4c2fc26b8e229a93bb4ad575', part: "circle", class: `bs-radio__circle ${this.checked ? 'bs-radio__circle--selected' : ''}` }, this.checked && h("span", { key: '18d9f81c031ff77ee4361a0ce8c73665bd6c1b75', part: "dot", class: "bs-radio__dot" })))), h("span", { key: 'a48cfc6d0689e523ab70450853b9fb2450c172e9', part: "label", class: "bs-radio__label" }, h("slot", { key: '7b1a801812fe510eb392c7266e8253bc9408c3db' }))));
107
+ return (h("label", { key: '84bfc39d5714d15c45936c8150b9bd7473b79574', class: `bs-radio ${this.disabled ? 'bs-radio--disabled' : ''}` }, h("input", { key: '0a118d5e59f4d33b0411f7be0fba08a706cda298', class: "bs-radio__input", type: "radio", name: this.name, value: this.value, checked: this.checked, disabled: this.disabled, tabIndex: this.checked || !this.groupHasChecked ? 0 : -1, onKeyDown: this.onKeyDown, onChange: this.onChange }), h("span", { key: '889dadd67126819c3d39a5823d48be58a9c71f9f', class: "bs-radio__hit-area" }, h("span", { key: '5bbed4d44bcf4d810693b5912a90855d680540d0', class: "bs-radio__state-layer" }, h("span", { key: '509389527b876127ca456900e3bb4aa21c735f72', part: "circle", class: `bs-radio__circle ${this.checked ? 'bs-radio__circle--selected' : ''}` }, this.checked && h("span", { key: '9a7e335f9646eb72fb06fb0ca9436e763c3ad278', part: "dot", class: "bs-radio__dot" })))), h("span", { key: '2017790d3d99355b4eb93e3659c809df58c61065', part: "label", class: "bs-radio__label" }, h("slot", { key: '199450bb26f7966cdd64b04c78e3015300d793c9' }))));
55
108
  }
56
109
  static get is() { return "bs-radio"; }
57
110
  static get encapsulation() { return "shadow"; }
58
111
  static get delegatesFocus() { return true; }
112
+ static get formAssociated() { return true; }
59
113
  static get originalStyleUrls() {
60
114
  return {
61
115
  "$": ["bs-radio.css"]
@@ -80,11 +134,11 @@ export class BsRadio {
80
134
  "optional": false,
81
135
  "docs": {
82
136
  "tags": [],
83
- "text": "The native radio input's `name` attribute. Passed straight through to the native input inside.\nNOTE: because each `bs-radio` renders its own shadow root, native `<input type=\"radio\">`\nmutual-exclusion grouping (which the HTML spec scopes to a single DOM tree) does NOT extend\nacross separate `bs-radio` instances sharing the same `name` -- checking one will not natively\nuncheck another. Within a single instance this still behaves like a normal radio input. See\nbs-radio.mdx's Accessibility section for this known gap; consumers building a group currently\nneed to manage mutual exclusivity themselves (e.g. via `bsChange`) until a `bs-radio-group`\nwrapper exists."
137
+ "text": "The native radio input's `name` attribute. Passed straight through to the native input inside.\nAlso the group key: `bs-radio` elements sharing a `name` within the same root (document or\nshadow root) behave as one radio group -- checking one unchecks the others, arrow keys move\nbetween them, and only one is in the tab order. Native grouping can't cross each radio's own\nshadow root, so this component implements it itself."
84
138
  },
85
139
  "getter": false,
86
140
  "setter": false,
87
- "reflect": false,
141
+ "reflect": true,
88
142
  "attribute": "name"
89
143
  },
90
144
  "value": {
@@ -142,12 +196,17 @@ export class BsRadio {
142
196
  },
143
197
  "getter": false,
144
198
  "setter": false,
145
- "reflect": false,
199
+ "reflect": true,
146
200
  "attribute": "disabled",
147
201
  "defaultValue": "false"
148
202
  }
149
203
  };
150
204
  }
205
+ static get states() {
206
+ return {
207
+ "groupHasChecked": {}
208
+ };
209
+ }
151
210
  static get events() {
152
211
  return [{
153
212
  "method": "bsChange",
@@ -157,7 +216,7 @@ export class BsRadio {
157
216
  "composed": true,
158
217
  "docs": {
159
218
  "tags": [],
160
- "text": "Emitted with this radio's own `value` when it becomes checked via user interaction. Since\nnative same-`name` grouping doesn't cross shadow-root boundaries (see the `name` prop's doc),\na consumer wiring up a group of `bs-radio`s must itself set `checked = false` on the others in\nresponse to this event -- there's no automatic un-checking to rely on here."
219
+ "text": "Emitted with this radio's own `value` when it becomes checked via user interaction. Since\nnative same-`name` grouping doesn't cross shadow-root boundaries (see the `name` prop's doc),\na consumer wiring up a group of `bs-radio`s does not need to un-check the others itself."
161
220
  },
162
221
  "complexType": {
163
222
  "original": "string",
@@ -167,4 +226,23 @@ export class BsRadio {
167
226
  }];
168
227
  }
169
228
  static get elementRef() { return "el"; }
229
+ static get watchers() {
230
+ return [{
231
+ "propName": "checked",
232
+ "methodName": "onCheckedChange"
233
+ }, {
234
+ "propName": "value",
235
+ "methodName": "onCheckedChange"
236
+ }];
237
+ }
238
+ static get listeners() {
239
+ return [{
240
+ "name": "bsChange",
241
+ "method": "onAnyRadioChange",
242
+ "target": "document",
243
+ "capture": false,
244
+ "passive": false
245
+ }];
246
+ }
247
+ static get attachInternalsMemberName() { return "internals"; }
170
248
  }
@@ -35,19 +35,10 @@ export const DisabledChecked = {
35
35
  name: 'Disabled + checked',
36
36
  args: { disabled: true, checked: true },
37
37
  };
38
- // Native same-name radio grouping doesn't extend across separate <bs-radio> instances (each has
39
- // its own shadow root -- see bs-radio.mdx's Accessibility section), so this story manages mutual
40
- // exclusivity itself by listening for bsChange and unchecking the siblings.
41
- const onGroupChange = (ev) => {
42
- const group = ev.target.closest('[data-radio-group]');
43
- group?.querySelectorAll('bs-radio').forEach(radio => {
44
- radio.checked = radio === ev.target;
45
- });
46
- };
47
38
  export const Group = {
48
39
  name: 'Radio group',
49
40
  render: () => html `
50
- <div data-radio-group style="display: flex; flex-direction: column; gap: 8px;" @bsChange=${onGroupChange}>
41
+ <div role="radiogroup" aria-label="Options" style="display: flex; flex-direction: column; gap: 8px;">
51
42
  <bs-radio name="group-story" value="option-1" checked>Option 1</bs-radio>
52
43
  <bs-radio name="group-story" value="option-2">Option 2</bs-radio>
53
44
  <bs-radio name="group-story" value="option-3">Option 3</bs-radio>