desy-html 16.0.3 → 17.0.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 (531) hide show
  1. package/.node-version +1 -1
  2. package/.nvmrc +1 -1
  3. package/AGENTS.md +207 -181
  4. package/README.md +203 -203
  5. package/TESTING_PLAN.md +379 -376
  6. package/branding/BRANDING.md +398 -395
  7. package/branding/branding.config.js +76 -76
  8. package/branding/branding.config.yourorganization.js +72 -65
  9. package/branding/logos/aragon-compact.svg +1 -1
  10. package/branding/logos/aragon-expanded.svg +1 -1
  11. package/branding/logos/aragon-mini.svg +1 -1
  12. package/branding/logos/favicon.svg +1 -1
  13. package/branding/logos/yourlogo-compact.svg +13 -13
  14. package/branding/logos/yourlogo-expanded.svg +17 -17
  15. package/branding/logos/yourlogo-mini.svg +17 -17
  16. package/branding/logos/yourorganization-apple-touch-icon.png +0 -0
  17. package/branding/logos/yourorganization-favicon.ico +0 -0
  18. package/branding/logos/yourorganization-favicon.svg +13 -0
  19. package/branding/vite-branding-plugin.js +221 -155
  20. package/docs/_global.content-placeholder.njk +7 -7
  21. package/docs/_global.foot.njk +11 -11
  22. package/docs/_global.head.njk +42 -43
  23. package/docs/_include.template-header.njk +391 -386
  24. package/docs/_macro.component-example.njk +6 -6
  25. package/docs/_macro.example-render.njk +350 -346
  26. package/docs/_macro.load-component-params.njk +2 -2
  27. package/docs/_macro.load-component-template.njk +3 -3
  28. package/docs/_macro.render-caller.njk +3 -3
  29. package/docs/_macro.show-code-from-file.njk +57 -57
  30. package/docs/_macro.show-html-from-file.njk +42 -42
  31. package/docs/_template.default.njk +8 -8
  32. package/docs/_template.examples.njk +13 -13
  33. package/docs/catalogo.html +27 -27
  34. package/docs/componentes.html +187 -184
  35. package/docs/ds/_ds.example.accordion.njk +109 -109
  36. package/docs/ds/_ds.example.border.njk +39 -39
  37. package/docs/ds/_ds.example.botones-alert-sm.njk +264 -264
  38. package/docs/ds/_ds.example.botones-alert.njk +264 -264
  39. package/docs/ds/_ds.example.botones-default-sm.njk +264 -264
  40. package/docs/ds/_ds.example.botones-default.njk +258 -258
  41. package/docs/ds/_ds.example.botones-primary-lg.njk +264 -264
  42. package/docs/ds/_ds.example.botones-primary-sm.njk +264 -264
  43. package/docs/ds/_ds.example.botones-primary.njk +264 -264
  44. package/docs/ds/_ds.example.botones-transparent-sm.njk +264 -264
  45. package/docs/ds/_ds.example.botones-transparent.njk +264 -264
  46. package/docs/ds/_ds.example.breadcrumbs.njk +65 -65
  47. package/docs/ds/_ds.example.card.njk +198 -198
  48. package/docs/ds/_ds.example.checkboxes.njk +377 -377
  49. package/docs/ds/_ds.example.collapsible.njk +18 -18
  50. package/docs/ds/_ds.example.color-de-interaccion.njk +16 -16
  51. package/docs/ds/_ds.example.colores-cabecera.njk +15 -15
  52. package/docs/ds/_ds.example.colores-de-soporte.njk +82 -82
  53. package/docs/ds/_ds.example.colores-neutros.njk +26 -26
  54. package/docs/ds/_ds.example.combobox.njk +91 -0
  55. package/docs/ds/_ds.example.date-input.njk +363 -363
  56. package/docs/ds/_ds.example.description-list.njk +289 -289
  57. package/docs/ds/_ds.example.details.njk +23 -23
  58. package/docs/ds/_ds.example.dropdowns-default.njk +186 -186
  59. package/docs/ds/_ds.example.dropdowns-en-uso.njk +105 -105
  60. package/docs/ds/_ds.example.dropdowns-variaciones.njk +224 -224
  61. package/docs/ds/_ds.example.error-summary.njk +34 -34
  62. package/docs/ds/_ds.example.file-upload.njk +54 -54
  63. package/docs/ds/_ds.example.footer.njk +28 -28
  64. package/docs/ds/_ds.example.header-advanced.njk +70 -70
  65. package/docs/ds/_ds.example.header.njk +211 -211
  66. package/docs/ds/_ds.example.input-group.njk +242 -242
  67. package/docs/ds/_ds.example.input.njk +164 -164
  68. package/docs/ds/_ds.example.item.njk +176 -176
  69. package/docs/ds/_ds.example.layout-escritorio.njk +38 -38
  70. package/docs/ds/_ds.example.layout-movil.njk +14 -14
  71. package/docs/ds/_ds.example.layout-sidebar.njk +11 -11
  72. package/docs/ds/_ds.example.links-list.njk +170 -170
  73. package/docs/ds/_ds.example.listbox-default.njk +677 -677
  74. package/docs/ds/_ds.example.listbox-variaciones.njk +824 -824
  75. package/docs/ds/_ds.example.menu-horizontal.njk +66 -66
  76. package/docs/ds/_ds.example.menu-navigation.njk +493 -493
  77. package/docs/ds/_ds.example.menu-vertical.njk +129 -129
  78. package/docs/ds/_ds.example.menubar-en-uso.njk +244 -244
  79. package/docs/ds/_ds.example.menubar-variaciones.njk +785 -785
  80. package/docs/ds/_ds.example.modal.njk +210 -210
  81. package/docs/ds/_ds.example.nav.njk +158 -158
  82. package/docs/ds/_ds.example.notification.njk +122 -122
  83. package/docs/ds/_ds.example.pagination.njk +190 -190
  84. package/docs/ds/_ds.example.pills.njk +215 -215
  85. package/docs/ds/_ds.example.radios.njk +419 -419
  86. package/docs/ds/_ds.example.searchbar.njk +87 -87
  87. package/docs/ds/_ds.example.select.njk +390 -390
  88. package/docs/ds/_ds.example.status-item.njk +239 -239
  89. package/docs/ds/_ds.example.status.njk +47 -47
  90. package/docs/ds/_ds.example.table.njk +740 -740
  91. package/docs/ds/_ds.example.tabs.njk +136 -136
  92. package/docs/ds/_ds.example.textarea.njk +237 -237
  93. package/docs/ds/_ds.example.textos.njk +32 -32
  94. package/docs/ds/_ds.example.toggle.njk +75 -75
  95. package/docs/ds/_ds.example.tree.njk +456 -456
  96. package/docs/ds/_ds.example.treegrid.njk +810 -810
  97. package/docs/ds/_ds.example.typography.njk +225 -225
  98. package/docs/ds/_ds.macro.code-snippet.njk +32 -32
  99. package/docs/ds/_ds.macro.section-title.njk +2 -2
  100. package/docs/ds/_ds.macro.subsection-title.njk +2 -2
  101. package/docs/ds/_ds.section.avisos.njk +22 -22
  102. package/docs/ds/_ds.section.botones.njk +53 -53
  103. package/docs/ds/_ds.section.campo-y-area-de-texto.njk +19 -19
  104. package/docs/ds/_ds.section.color.njk +147 -147
  105. package/docs/ds/_ds.section.datos.njk +42 -42
  106. package/docs/ds/_ds.section.dropdowns.njk +23 -23
  107. package/docs/ds/_ds.section.espaciado.njk +296 -296
  108. package/docs/ds/_ds.section.forms.njk +73 -68
  109. package/docs/ds/_ds.section.informacion.njk +12 -12
  110. package/docs/ds/_ds.section.layout.njk +63 -63
  111. package/docs/ds/_ds.section.menubar.njk +18 -18
  112. package/docs/ds/_ds.section.mostrar-ocultar.njk +33 -33
  113. package/docs/ds/_ds.section.navigation.njk +57 -57
  114. package/docs/ds/_ds.section.textos.njk +287 -287
  115. package/docs/ds/_ds.section.typography.njk +28 -28
  116. package/docs/estilos.html +37 -37
  117. package/docs/examples-accordion-history.html +8 -8
  118. package/docs/examples-accordion.html +8 -8
  119. package/docs/examples-alert.html +8 -8
  120. package/docs/examples-breadcrumbs.html +8 -8
  121. package/docs/examples-button-loader.html +8 -8
  122. package/docs/examples-button.html +8 -8
  123. package/docs/examples-card.html +8 -8
  124. package/docs/examples-character-count.html +8 -8
  125. package/docs/examples-checkboxes.html +8 -8
  126. package/docs/examples-collapsible.html +8 -8
  127. package/docs/examples-combobox.html +8 -0
  128. package/docs/examples-date-input.html +8 -8
  129. package/docs/examples-datepicker.html +8 -8
  130. package/docs/examples-description-list.html +8 -8
  131. package/docs/examples-details.html +8 -8
  132. package/docs/examples-dialog.html +8 -8
  133. package/docs/examples-dropdown.html +8 -8
  134. package/docs/examples-error-message.html +8 -8
  135. package/docs/examples-error-summary.html +8 -8
  136. package/docs/examples-fieldset.html +8 -8
  137. package/docs/examples-file-upload.html +8 -8
  138. package/docs/examples-footer.html +8 -8
  139. package/docs/examples-header-advanced.html +8 -8
  140. package/docs/examples-header-mini.html +8 -8
  141. package/docs/examples-header.html +8 -8
  142. package/docs/examples-hint.html +8 -8
  143. package/docs/examples-input-group.html +8 -8
  144. package/docs/examples-input.html +8 -8
  145. package/docs/examples-item.html +8 -8
  146. package/docs/examples-label.html +8 -8
  147. package/docs/examples-links-list.html +8 -8
  148. package/docs/examples-listbox.html +8 -8
  149. package/docs/examples-media-object.html +8 -8
  150. package/docs/examples-menu-horizontal.html +8 -8
  151. package/docs/examples-menu-navigation.html +8 -8
  152. package/docs/examples-menu-vertical.html +8 -8
  153. package/docs/examples-menubar.html +8 -8
  154. package/docs/examples-modal.html +8 -8
  155. package/docs/examples-nav.html +8 -8
  156. package/docs/examples-notification.html +8 -8
  157. package/docs/examples-pagination.html +8 -8
  158. package/docs/examples-pill.html +8 -8
  159. package/docs/examples-radios.html +8 -8
  160. package/docs/examples-searchbar.html +8 -8
  161. package/docs/examples-select.html +8 -8
  162. package/docs/examples-skip-link.html +8 -8
  163. package/docs/examples-spinner.html +8 -8
  164. package/docs/examples-status-item.html +8 -8
  165. package/docs/examples-status.html +8 -8
  166. package/docs/examples-table-advanced.html +8 -8
  167. package/docs/examples-table.html +8 -8
  168. package/docs/examples-tabs.html +8 -8
  169. package/docs/examples-textarea.html +8 -8
  170. package/docs/examples-toggle.html +8 -8
  171. package/docs/examples-tooltip.html +8 -8
  172. package/docs/examples-tree.html +8 -8
  173. package/docs/examples-treegrid.html +8 -8
  174. package/docs/index.html +914 -902
  175. package/docs/pagina-accesibilidad.html +110 -110
  176. package/docs/pagina-mapa-web.html +137 -137
  177. package/docs/pagina-prueba.html +95 -95
  178. package/docs/plantilla-con-header-advanced.html +14 -14
  179. package/docs/plantilla-editar-con-cabecera-fija-y-sidebar-sticky.html +154 -154
  180. package/docs/plantilla-editar-con-cabecera-fija.html +17 -17
  181. package/docs/plantilla-logueado-con-cabecera-fija-headroom.html +15 -15
  182. package/docs/plantilla-logueado-con-cabecera-fija.html +15 -15
  183. package/docs/plantilla-logueado-con-selector-de-app-y-sidebar.html +164 -164
  184. package/docs/plantilla-logueado-con-selector-de-app-y-subheader.html +16 -16
  185. package/docs/plantilla-logueado-con-selector-de-app.html +14 -14
  186. package/docs/plantilla-logueado-con-titulo-de-app.html +14 -14
  187. package/docs/plantilla-sin-loguear.html +14 -14
  188. package/docs/plantillas.html +89 -89
  189. package/docs/spinner-plantilla-con-header-advanced.html +2 -2
  190. package/docs/spinner-plantilla-editar-con-cabecera-fija.html +4 -4
  191. package/docs/spinner-plantilla-logueado-con-cabecera-fija.html +2 -2
  192. package/docs/spinner-plantilla-logueado-con-selector-de-app-y-subheader.html +2 -2
  193. package/docs/spinner-plantilla-logueado-con-titulo-de-app.html +2 -2
  194. package/docs/spinner-plantilla-sin-loguear.html +18 -18
  195. package/docs/sprints/01-combobox-template.md +433 -0
  196. package/docs/sprints/02-combobox-styles.md +291 -0
  197. package/docs/sprints/03-combobox-javascript.md +589 -0
  198. package/docs/sprints/04-combobox-integration.md +386 -0
  199. package/docs/sprints/05-combobox-refinamiento-w3c.md +55 -0
  200. package/package.json +61 -58
  201. package/public/images/demo-icon.svg +9 -0
  202. package/replit.md +74 -74
  203. package/src/EUPL-1.2.txt +287 -287
  204. package/src/css/base.css +44 -44
  205. package/src/css/component.form-group.css +24 -24
  206. package/src/css/component.headroom.css +32 -32
  207. package/src/css/component.text.css +169 -169
  208. package/src/css/styles.css +247 -246
  209. package/src/js/aria/MenuHorizontal.js +65 -65
  210. package/src/js/aria/MenuNavigation.js +34 -2
  211. package/src/js/aria/MenuVertical.js +60 -60
  212. package/src/js/aria/Nav.js +60 -60
  213. package/src/js/aria/PopupMenuAction.js +30 -12
  214. package/src/js/aria/accordion.js +264 -264
  215. package/src/js/aria/checkBoxes.js +52 -52
  216. package/src/js/aria/collapsible.js +44 -44
  217. package/src/js/aria/combobox.js +584 -0
  218. package/src/js/aria/dataGrid.js +950 -950
  219. package/src/js/aria/linksList.js +42 -42
  220. package/src/js/aria/notification.js +56 -56
  221. package/src/js/aria/radioButton.js +50 -50
  222. package/src/js/aria/toggle.js +61 -61
  223. package/src/js/aria/treegrid.js +507 -507
  224. package/src/js/aria/utils.js +219 -219
  225. package/src/js/desy-html.js +860 -813
  226. package/src/js/headroom.min.js +6 -6
  227. package/src/js/index.js +74 -72
  228. package/src/main.js +2 -2
  229. package/src/templates/components/accordion/_examples.accordion.njk +482 -482
  230. package/src/templates/components/accordion/_macro.accordion.njk +3 -3
  231. package/src/templates/components/accordion/_template.accordion.njk +131 -131
  232. package/src/templates/components/accordion/params.accordion.yaml +125 -125
  233. package/src/templates/components/accordion-history/_examples.accordion-history.njk +521 -521
  234. package/src/templates/components/accordion-history/_macro.accordion-history.njk +3 -3
  235. package/src/templates/components/accordion-history/_template.accordion-history.njk +193 -193
  236. package/src/templates/components/accordion-history/params.accordion-history.yaml +129 -129
  237. package/src/templates/components/alert/_examples.alert.njk +91 -91
  238. package/src/templates/components/alert/_macro.alert.njk +3 -3
  239. package/src/templates/components/alert/_styles.alert.css +9 -9
  240. package/src/templates/components/alert/_template.alert.njk +16 -16
  241. package/src/templates/components/alert/params.alert.yaml +25 -25
  242. package/src/templates/components/breadcrumbs/_examples.breadcrumbs.njk +464 -464
  243. package/src/templates/components/breadcrumbs/_macro.breadcrumbs.njk +3 -3
  244. package/src/templates/components/breadcrumbs/_styles.breadcrumbs.css +81 -81
  245. package/src/templates/components/breadcrumbs/_template.breadcrumbs.njk +77 -77
  246. package/src/templates/components/breadcrumbs/params.breadcrumbs.yaml +44 -44
  247. package/src/templates/components/button/_examples.button.njk +518 -511
  248. package/src/templates/components/button/_macro.button.njk +3 -3
  249. package/src/templates/components/button/_styles.button.css +180 -180
  250. package/src/templates/components/button/_template.button.njk +49 -49
  251. package/src/templates/components/button/params.button.yaml +48 -48
  252. package/src/templates/components/button-loader/_examples.button-loader.njk +499 -492
  253. package/src/templates/components/button-loader/_macro.button-loader.njk +3 -3
  254. package/src/templates/components/button-loader/_styles.button-loader.css +194 -194
  255. package/src/templates/components/button-loader/_template.button-loader.njk +84 -84
  256. package/src/templates/components/button-loader/params.button-loader.yaml +74 -74
  257. package/src/templates/components/card/_examples.card.njk +381 -381
  258. package/src/templates/components/card/_macro.card.njk +3 -3
  259. package/src/templates/components/card/_template.card.njk +34 -34
  260. package/src/templates/components/card/params.card.yaml +112 -112
  261. package/src/templates/components/character-count/_examples.character-count.njk +195 -195
  262. package/src/templates/components/character-count/_macro.character-count.njk +5 -5
  263. package/src/templates/components/character-count/_template.character-count.njk +38 -38
  264. package/src/templates/components/character-count/params.character-count.yaml +77 -77
  265. package/src/templates/components/checkboxes/_examples.checkboxes.njk +835 -835
  266. package/src/templates/components/checkboxes/_macro.checkboxes.njk +3 -3
  267. package/src/templates/components/checkboxes/_styles.checkboxes.css +30 -30
  268. package/src/templates/components/checkboxes/_template.checkboxes.njk +137 -137
  269. package/src/templates/components/checkboxes/params.checkboxes.yaml +116 -116
  270. package/src/templates/components/collapsible/_examples.collapsible.njk +107 -107
  271. package/src/templates/components/collapsible/_macro.collapsible.njk +3 -3
  272. package/src/templates/components/collapsible/_styles.collapsible.css +35 -35
  273. package/src/templates/components/collapsible/_template.collapsible.njk +17 -17
  274. package/src/templates/components/collapsible/params.collapsible.yaml +48 -48
  275. package/src/templates/components/combobox/_examples.combobox.njk +440 -0
  276. package/src/templates/components/combobox/_macro.combobox.njk +3 -0
  277. package/src/templates/components/combobox/_styles.combobox.css +92 -0
  278. package/src/templates/components/combobox/_template.combobox.njk +97 -0
  279. package/src/templates/components/combobox/params.combobox.yaml +102 -0
  280. package/src/templates/components/date-input/_examples.date-input.njk +578 -578
  281. package/src/templates/components/date-input/_macro.date-input.njk +3 -3
  282. package/src/templates/components/date-input/_template.date-input.njk +125 -125
  283. package/src/templates/components/date-input/params.date-input.yaml +97 -97
  284. package/src/templates/components/datepicker/_examples.datepicker.njk +415 -415
  285. package/src/templates/components/datepicker/_macro.datepicker.njk +3 -3
  286. package/src/templates/components/datepicker/_styles.datepicker.css +87 -87
  287. package/src/templates/components/datepicker/_template.datepicker.njk +129 -129
  288. package/src/templates/components/datepicker/params.datepicker.yaml +104 -104
  289. package/src/templates/components/description-list/_examples.description-list.njk +502 -502
  290. package/src/templates/components/description-list/_macro.description-list.njk +3 -3
  291. package/src/templates/components/description-list/_template.description-list.njk +17 -17
  292. package/src/templates/components/description-list/params.description-list.yaml +61 -61
  293. package/src/templates/components/details/_examples.details.njk +68 -68
  294. package/src/templates/components/details/_macro.details.njk +3 -3
  295. package/src/templates/components/details/_template.details.njk +17 -17
  296. package/src/templates/components/details/params.details.yaml +40 -40
  297. package/src/templates/components/dialog/_examples.dialog.njk +156 -156
  298. package/src/templates/components/dialog/_macro.dialog.njk +3 -3
  299. package/src/templates/components/dialog/_styles.dialog.css +27 -27
  300. package/src/templates/components/dialog/_template.dialog.njk +12 -12
  301. package/src/templates/components/dialog/params.dialog.yaml +25 -25
  302. package/src/templates/components/dropdown/_examples.dropdown.njk +226 -226
  303. package/src/templates/components/dropdown/_macro.dropdown.njk +3 -3
  304. package/src/templates/components/dropdown/_styles.dropdown.css +191 -191
  305. package/src/templates/components/dropdown/_template.dropdown.njk +34 -34
  306. package/src/templates/components/dropdown/params.dropdown.yaml +32 -32
  307. package/src/templates/components/error-message/_examples.error-message.njk +28 -28
  308. package/src/templates/components/error-message/_macro.error-message.njk +5 -5
  309. package/src/templates/components/error-message/_template.error-message.njk +8 -8
  310. package/src/templates/components/error-message/params.error-message.yaml +23 -23
  311. package/src/templates/components/error-summary/_examples.error-summary.njk +111 -111
  312. package/src/templates/components/error-summary/_macro.error-summary.njk +5 -5
  313. package/src/templates/components/error-summary/_template.error-summary.njk +48 -48
  314. package/src/templates/components/error-summary/params.error-summary.yaml +50 -50
  315. package/src/templates/components/fieldset/_examples.fieldset.njk +79 -79
  316. package/src/templates/components/fieldset/_macro.fieldset.njk +2 -2
  317. package/src/templates/components/fieldset/_template.fieldset.njk +46 -46
  318. package/src/templates/components/fieldset/params.fieldset.yaml +49 -49
  319. package/src/templates/components/file-upload/_examples.file-upload.njk +103 -103
  320. package/src/templates/components/file-upload/_macro.file-upload.njk +2 -2
  321. package/src/templates/components/file-upload/_template.file-upload.njk +45 -45
  322. package/src/templates/components/file-upload/params.file-upload.yaml +48 -48
  323. package/src/templates/components/footer/_examples.footer.njk +558 -558
  324. package/src/templates/components/footer/_macro.footer.njk +3 -3
  325. package/src/templates/components/footer/_styles.footer.css +43 -43
  326. package/src/templates/components/footer/_template.footer.njk +130 -130
  327. package/src/templates/components/footer/params.footer.yaml +140 -140
  328. package/src/templates/components/header/_examples.header.njk +627 -627
  329. package/src/templates/components/header/_macro.header.header__dropdown.njk +3 -3
  330. package/src/templates/components/header/_macro.header.header__navigation.njk +3 -3
  331. package/src/templates/components/header/_macro.header.header__offcanvas.njk +3 -3
  332. package/src/templates/components/header/_macro.header.header__offcanvasButton.njk +3 -3
  333. package/src/templates/components/header/_macro.header.header__subnav.njk +3 -3
  334. package/src/templates/components/header/_macro.header.njk +3 -3
  335. package/src/templates/components/header/_styles.header.css +12 -12
  336. package/src/templates/components/header/_template.header.header__dropdown.njk +27 -27
  337. package/src/templates/components/header/_template.header.header__navigation.njk +25 -25
  338. package/src/templates/components/header/_template.header.header__offcanvas.njk +20 -20
  339. package/src/templates/components/header/_template.header.header__offcanvasButton.njk +10 -10
  340. package/src/templates/components/header/_template.header.header__subnav.njk +33 -33
  341. package/src/templates/components/header/_template.header.njk +155 -155
  342. package/src/templates/components/header/params.header.yaml +295 -295
  343. package/src/templates/components/header-advanced/_examples.header-advanced.njk +1110 -1110
  344. package/src/templates/components/header-advanced/_macro.header-advanced.njk +3 -3
  345. package/src/templates/components/header-advanced/_template.header-advanced.njk +171 -171
  346. package/src/templates/components/header-advanced/params.header-advanced.yaml +352 -350
  347. package/src/templates/components/header-mini/_examples.header-mini.njk +319 -316
  348. package/src/templates/components/header-mini/_macro.header-mini.njk +3 -3
  349. package/src/templates/components/header-mini/_template.header-mini.njk +51 -51
  350. package/src/templates/components/header-mini/params.header-mini.yaml +90 -90
  351. package/src/templates/components/hint/_examples.hint.njk +26 -26
  352. package/src/templates/components/hint/_macro.hint.njk +3 -3
  353. package/src/templates/components/hint/_template.hint.njk +3 -3
  354. package/src/templates/components/hint/params.hint.yaml +21 -21
  355. package/src/templates/components/input/_examples.input.njk +393 -393
  356. package/src/templates/components/input/_macro.input.njk +3 -3
  357. package/src/templates/components/input/_styles.input.css +17 -17
  358. package/src/templates/components/input/_template.input.njk +53 -53
  359. package/src/templates/components/input/params.input.yaml +80 -80
  360. package/src/templates/components/input-group/_examples.input-group.njk +558 -558
  361. package/src/templates/components/input-group/_macro.input-group.njk +3 -3
  362. package/src/templates/components/input-group/_template.input-group.njk +107 -107
  363. package/src/templates/components/input-group/params.input-group.yaml +123 -123
  364. package/src/templates/components/item/_examples.item.njk +370 -370
  365. package/src/templates/components/item/_macro.item.njk +3 -3
  366. package/src/templates/components/item/_template.item.njk +73 -73
  367. package/src/templates/components/item/params.item.yaml +101 -101
  368. package/src/templates/components/label/_examples.label.njk +58 -58
  369. package/src/templates/components/label/_macro.label.njk +5 -5
  370. package/src/templates/components/label/_template.label.njk +37 -37
  371. package/src/templates/components/label/params.label.yaml +28 -28
  372. package/src/templates/components/links-list/_examples.links-list.njk +761 -761
  373. package/src/templates/components/links-list/_macro.links-list.njk +3 -3
  374. package/src/templates/components/links-list/_template.links-list.njk +119 -119
  375. package/src/templates/components/links-list/params.links-list.yaml +101 -101
  376. package/src/templates/components/listbox/_examples.listbox.njk +813 -812
  377. package/src/templates/components/listbox/_macro.listbox.njk +3 -3
  378. package/src/templates/components/listbox/_styles.listbox.css +218 -218
  379. package/src/templates/components/listbox/_template.listbox.njk +90 -90
  380. package/src/templates/components/listbox/params.listbox.yaml +85 -85
  381. package/src/templates/components/media-object/_examples.media-object.njk +79 -79
  382. package/src/templates/components/media-object/_macro.media-object.njk +3 -3
  383. package/src/templates/components/media-object/_template.media-object.njk +21 -21
  384. package/src/templates/components/media-object/params.media-object.yaml +16 -16
  385. package/src/templates/components/menu-horizontal/_examples.menu-horizontal.njk +613 -613
  386. package/src/templates/components/menu-horizontal/_macro.menu-horizontal.njk +3 -3
  387. package/src/templates/components/menu-horizontal/_styles.menu-horizontal.css +167 -167
  388. package/src/templates/components/menu-horizontal/_template.menu-horizontal.njk +36 -36
  389. package/src/templates/components/menu-horizontal/params.menu-horizontal.yaml +53 -53
  390. package/src/templates/components/menu-navigation/_examples.menu-navigation.njk +1292 -1292
  391. package/src/templates/components/menu-navigation/_macro.menu-navigation.njk +3 -3
  392. package/src/templates/components/menu-navigation/_styles.menu-navigation.css +238 -238
  393. package/src/templates/components/menu-navigation/_template.menu-navigation.njk +87 -87
  394. package/src/templates/components/menu-navigation/params.menu-navigation.yaml +86 -86
  395. package/src/templates/components/menu-vertical/_examples.menu-vertical.njk +835 -835
  396. package/src/templates/components/menu-vertical/_macro.menu-vertical.njk +3 -3
  397. package/src/templates/components/menu-vertical/_template.menu-vertical.njk +92 -92
  398. package/src/templates/components/menu-vertical/params.menu-vertical.yaml +77 -77
  399. package/src/templates/components/menubar/_examples.menubar.njk +1843 -1843
  400. package/src/templates/components/menubar/_macro.menubar.njk +3 -3
  401. package/src/templates/components/menubar/_styles.menubar.css +259 -259
  402. package/src/templates/components/menubar/_template.menubar.njk +106 -106
  403. package/src/templates/components/menubar/params.menubar.yaml +130 -130
  404. package/src/templates/components/modal/_examples.modal.njk +432 -432
  405. package/src/templates/components/modal/_macro.modal.njk +3 -3
  406. package/src/templates/components/modal/_template.modal.njk +129 -129
  407. package/src/templates/components/modal/params.modal.yaml +77 -77
  408. package/src/templates/components/nav/_examples.nav.njk +468 -468
  409. package/src/templates/components/nav/_macro.nav.njk +3 -3
  410. package/src/templates/components/nav/_template.nav.njk +53 -53
  411. package/src/templates/components/nav/params.nav.yaml +61 -61
  412. package/src/templates/components/notification/_examples.notification.njk +277 -277
  413. package/src/templates/components/notification/_macro.notification.njk +3 -3
  414. package/src/templates/components/notification/_styles.notification.css +30 -30
  415. package/src/templates/components/notification/_template.notification.njk +86 -86
  416. package/src/templates/components/notification/params.notification.yaml +101 -101
  417. package/src/templates/components/pagination/_examples.pagination.njk +444 -444
  418. package/src/templates/components/pagination/_macro.pagination.njk +3 -3
  419. package/src/templates/components/pagination/_template.pagination.njk +162 -162
  420. package/src/templates/components/pagination/params.pagination.yaml +150 -150
  421. package/src/templates/components/pill/_examples.pill.njk +180 -180
  422. package/src/templates/components/pill/_macro.pill.njk +3 -3
  423. package/src/templates/components/pill/_styles.pill.css +59 -59
  424. package/src/templates/components/pill/_template.pill.njk +38 -38
  425. package/src/templates/components/pill/params.pill.yaml +28 -28
  426. package/src/templates/components/radios/_examples.radios.njk +698 -698
  427. package/src/templates/components/radios/_macro.radios.njk +5 -5
  428. package/src/templates/components/radios/_styles.radios.css +30 -30
  429. package/src/templates/components/radios/_template.radios.njk +133 -133
  430. package/src/templates/components/radios/params.radios.yaml +104 -104
  431. package/src/templates/components/searchbar/_examples.searchbar.njk +147 -147
  432. package/src/templates/components/searchbar/_macro.searchbar.njk +3 -3
  433. package/src/templates/components/searchbar/_template.searchbar.njk +45 -45
  434. package/src/templates/components/searchbar/params.searchbar.yaml +46 -46
  435. package/src/templates/components/select/_examples.select.njk +364 -364
  436. package/src/templates/components/select/_macro.select.njk +5 -5
  437. package/src/templates/components/select/_styles.select.css +45 -45
  438. package/src/templates/components/select/_template.select.njk +67 -67
  439. package/src/templates/components/select/params.select.yaml +93 -93
  440. package/src/templates/components/skip-link/_examples.skip-link.njk +31 -31
  441. package/src/templates/components/skip-link/_macro.skip-link.njk +3 -3
  442. package/src/templates/components/skip-link/_styles.skip-link.css +12 -12
  443. package/src/templates/components/skip-link/_template.skip-link.njk +4 -4
  444. package/src/templates/components/skip-link/params.skip-link.yaml +20 -20
  445. package/src/templates/components/spinner/_examples.spinner.njk +91 -101
  446. package/src/templates/components/spinner/_macro.spinner.njk +3 -3
  447. package/src/templates/components/spinner/_styles.spinner.css +33 -33
  448. package/src/templates/components/spinner/_template.spinner.njk +12 -12
  449. package/src/templates/components/spinner/params.spinner.yaml +12 -12
  450. package/src/templates/components/status/_examples.status.njk +80 -80
  451. package/src/templates/components/status/_macro.status.njk +3 -3
  452. package/src/templates/components/status/_template.status.njk +23 -23
  453. package/src/templates/components/status/params.status.yaml +24 -24
  454. package/src/templates/components/status-item/_examples.status-item.njk +319 -319
  455. package/src/templates/components/status-item/_macro.status-item.njk +3 -3
  456. package/src/templates/components/status-item/_template.status-item.njk +99 -99
  457. package/src/templates/components/status-item/params.status-item.yaml +106 -106
  458. package/src/templates/components/table/_examples.table.njk +705 -705
  459. package/src/templates/components/table/_macro.table.njk +3 -3
  460. package/src/templates/components/table/_styles.table.css +19 -19
  461. package/src/templates/components/table/_template.table.njk +68 -68
  462. package/src/templates/components/table/params.table.yaml +130 -130
  463. package/src/templates/components/table-advanced/_examples.table-advanced.njk +839 -839
  464. package/src/templates/components/table-advanced/_macro.table-advanced.njk +3 -3
  465. package/src/templates/components/table-advanced/_styles.table-advanced.css +63 -63
  466. package/src/templates/components/table-advanced/_template.table-advanced.njk +210 -210
  467. package/src/templates/components/table-advanced/params.table-advanced.yaml +160 -160
  468. package/src/templates/components/tabs/_examples.tabs.njk +588 -588
  469. package/src/templates/components/tabs/_macro.tabs.njk +3 -3
  470. package/src/templates/components/tabs/_styles.tabs.css +132 -132
  471. package/src/templates/components/tabs/_template.tabs.njk +70 -70
  472. package/src/templates/components/tabs/params.tabs.yaml +83 -83
  473. package/src/templates/components/textarea/_examples.textarea.njk +174 -174
  474. package/src/templates/components/textarea/_macro.textarea.njk +5 -5
  475. package/src/templates/components/textarea/_template.textarea.njk +47 -47
  476. package/src/templates/components/textarea/params.textarea.yaml +64 -64
  477. package/src/templates/components/toggle/_examples.toggle.njk +248 -248
  478. package/src/templates/components/toggle/_macro.toggle.njk +3 -3
  479. package/src/templates/components/toggle/_styles.toggle.css +31 -31
  480. package/src/templates/components/toggle/_template.toggle.njk +27 -27
  481. package/src/templates/components/toggle/params.toggle.yaml +69 -69
  482. package/src/templates/components/tooltip/_examples.tooltip.njk +134 -134
  483. package/src/templates/components/tooltip/_macro.tooltip.njk +3 -3
  484. package/src/templates/components/tooltip/_styles.tooltip.css +9 -9
  485. package/src/templates/components/tooltip/_template.tooltip.njk +40 -40
  486. package/src/templates/components/tooltip/params.tooltip.yaml +40 -40
  487. package/src/templates/components/tree/_examples.tree.njk +2671 -2671
  488. package/src/templates/components/tree/_macro.tree.njk +3 -3
  489. package/src/templates/components/tree/_styles.tree.css +54 -54
  490. package/src/templates/components/tree/_template.tree.njk +232 -232
  491. package/src/templates/components/tree/params.tree.yaml +173 -173
  492. package/src/templates/components/treegrid/_examples.treegrid.njk +1269 -1269
  493. package/src/templates/components/treegrid/_macro.treegrid.njk +3 -3
  494. package/src/templates/components/treegrid/_styles.treegrid.css +31 -31
  495. package/src/templates/components/treegrid/_template.treegrid.njk +90 -90
  496. package/src/templates/components/treegrid/params.treegrid.yaml +131 -131
  497. package/src/templates/includes/_abrir-notificaciones-extra.njk +2 -2
  498. package/src/templates/includes/_abrir-notificaciones.njk +2 -2
  499. package/src/templates/includes/_acciones-de-cabecera.njk +28 -28
  500. package/src/templates/includes/_ejemplo-titulo-h2-parrafo.njk +4 -4
  501. package/src/templates/includes/_ejemplo-titulo-parrafo.njk +4 -4
  502. package/src/templates/includes/_test-include.njk +13 -13
  503. package/src/templates/pages/_page.foot-headroom.njk +30 -30
  504. package/src/templates/pages/_page.foot.njk +5 -5
  505. package/src/templates/pages/_page.footer.njk +19 -19
  506. package/src/templates/pages/_page.head.njk +27 -27
  507. package/src/templates/pages/_page.notification-edit-inner-content.njk +31 -31
  508. package/src/templates/pages/_page.notification-edit.njk +30 -30
  509. package/src/templates/pages/_page.notification-footer.njk +32 -32
  510. package/src/templates/pages/_page.notification-header-fixed.njk +30 -30
  511. package/src/templates/pages/_page.notification-header.njk +31 -31
  512. package/src/templates/pages/_page.sidebar-content.njk +20 -20
  513. package/src/templates/pages/_page.spinner-block.njk +14 -14
  514. package/src/templates/pages/_page.spinner-show.njk +15 -15
  515. package/src/templates/pages/_page.spinner.njk +15 -15
  516. package/src/templates/pages/_template.edit-fixed-with-sticky-sidebar.njk +36 -36
  517. package/src/templates/pages/_template.edit-fixed.njk +74 -74
  518. package/src/templates/pages/_template.home.njk +111 -111
  519. package/src/templates/pages/_template.logged-out.njk +56 -56
  520. package/src/templates/pages/_template.logged-selector-fixed-headroom.njk +198 -198
  521. package/src/templates/pages/_template.logged-selector-fixed.njk +195 -195
  522. package/src/templates/pages/_template.logged-selector-subheader.njk +80 -80
  523. package/src/templates/pages/_template.logged-selector-with-sidebar.njk +28 -28
  524. package/src/templates/pages/_template.logged-selector.njk +195 -195
  525. package/src/templates/pages/_template.logged.njk +141 -141
  526. package/src/templates/pages/_template.mfe-iframe-content.njk +31 -31
  527. package/src/templates/pages/_template.mfe.njk +83 -83
  528. package/src/templates/pages/_template.test.njk +48 -48
  529. package/src/templates/pages/_template.with-header-advanced.njk +311 -311
  530. package/vite.config.js +982 -955
  531. package/public/images/logo-educa-aragon.svg +0 -1
@@ -0,0 +1,589 @@
1
+ # Fase 3: Combobox - JavaScript ARIA
2
+
3
+ ## Deliverables
4
+
5
+ | Archivo | Accion |
6
+ |---------|--------|
7
+ | `src/js/aria/combobox.js` | Crear |
8
+
9
+ ## Dependencias
10
+
11
+ - **Fase 1 completada**: Templates HTML con `data-module` attributes
12
+ - **Fase 2 completada**: Estilos CSS disponibles
13
+
14
+ ## PROMPT
15
+
16
+ ```
17
+ Eres un desarrollador JavaScript especializado en accesibilidad web y patrones ARIA. Vas a crear el modulo JS para el componente combobox con autocomplete en desy-html (Design System del Gobierno de Aragon).
18
+
19
+ ## Contexto del proyecto
20
+
21
+ desy-html usa un patron de prototipos para componentes ARIA (no clases ES6). Cada modulo exporta una funcion que recibe un objeto `aria` y le anade constructores/prototipos. Los componentes se inicializan desde `desy-html.js` buscando elementos `[data-module]`.
22
+
23
+ ## MCP Server DESY
24
+
25
+ Antes de implementar, consulta las guias del design system:
26
+ 1. Usa `mcp__DESY-MCP-Server__get_guideline` para obtener guias de:
27
+ - Interaccion y teclado en componentes
28
+ - Patrones ARIA para combobox/listbox
29
+ - Accesibilidad en formularios
30
+
31
+ ## Skills y agentes disponibles
32
+
33
+ - Usa la skill `desy` para consultar patrones del design system
34
+ - Usa la skill `ux-designer` para validar decisiones de interaccion
35
+ - Al finalizar, usa los agentes:
36
+ - `code-simplifier:code-simplifier` para simplificar el codigo
37
+ - `code-refactoring:code-reviewer` para revisar calidad
38
+
39
+ ## Patron W3C de referencia
40
+
41
+ Patron WAI-ARIA APG "combobox-autocomplete-both":
42
+ - URL: https://www.w3.org/WAI/ARIA/apg/patterns/combobox/
43
+ - Keyboard: Arrow Down/Up, Enter, Escape, Home, End, Alt+Down
44
+ - Focus management: aria-activedescendant en el input (no mover focus al listbox)
45
+ - Inline completion (modo "both"): setSelectionRange() para autocompletar texto
46
+
47
+ ## Decisiones de diseno
48
+
49
+ | Aspecto | Decision |
50
+ |---------|----------|
51
+ | Seleccion | Unica |
52
+ | Autocomplete | 'list' y 'both' (configurable) |
53
+ | Filtrado | Starts-with con normalizacion de acentos |
54
+ | Blur sin match | Limpiar input y hidden value |
55
+ | Posicionamiento popup | createFloatingElement() existente |
56
+ | Scroll pagina | Cierra popup |
57
+ | Foco post-seleccion | Vuelve al input |
58
+ | Eventos custom | combobox:select, combobox:clear, combobox:open, combobox:close |
59
+ | Inline completion | setSelectionRange() nativo (modo "both") |
60
+ | Clear button | Siempre visible cuando hay texto, limpia input + hidden + cierra popup |
61
+
62
+ ## Archivos de referencia
63
+
64
+ ### Patron ARIA con prototipos:
65
+ Archivo: `src/js/aria/listbox.js`
66
+
67
+ Estructura del modulo:
68
+ ```js
69
+ export function listbox(aria) {
70
+ // Constructor
71
+ aria.Listbox = function (listboxNode) {
72
+ this.listboxNode = listboxNode;
73
+ this.activeDescendant = this.listboxNode.getAttribute('aria-activedescendant');
74
+ // ...
75
+ this.registerEvents();
76
+ };
77
+
78
+ // Metodos via prototype
79
+ aria.Listbox.prototype.registerEvents = function () { ... };
80
+ aria.Listbox.prototype.focusItem = function (element) { ... };
81
+ // etc.
82
+
83
+ return aria;
84
+ }
85
+ ```
86
+
87
+ Aspectos clave del listbox.js:
88
+ - Usa `aria.KeyCode` para constantes de teclas (UP, DOWN, HOME, END, SPACE, RETURN, ESC)
89
+ - Maneja `aria-activedescendant` para focus visual sin mover focus real
90
+ - Scroll automatico: compara offsetTop/offsetHeight con scrollTop/clientHeight
91
+ - Patron findMatchInRange para busqueda por texto
92
+
93
+ ### createFloatingElement() en desy-html.js:
94
+ Archivo: `src/js/desy-html.js` (linea 509)
95
+
96
+ ```js
97
+ function createFloatingElement(reference, content, options = {}) {
98
+ // Options relevantes para combobox:
99
+ // placement: 'bottom-start'
100
+ // interactive: true
101
+ // trigger: 'manual'
102
+ // hideOnClick: true
103
+ // hideOnEsc: true
104
+ // hideOnPopperBlur: true
105
+ // onMount, onShown, onHidden callbacks
106
+
107
+ // Retorna API: { show, hide, toggle, update, isShown, destroy }
108
+ }
109
+ ```
110
+
111
+ Ejemplo de uso en listboxComponent (linea 138):
112
+ ```js
113
+ const instance = createFloatingElement(buttonListbox, tooltip, {
114
+ placement: 'bottom-start',
115
+ offset: [8, -1],
116
+ middleware: middlewares,
117
+ interactive: true,
118
+ trigger: 'manual',
119
+ hideOnClick: true,
120
+ role: false,
121
+ ariaContent: 'auto',
122
+ hideOnEsc: true,
123
+ hideOnPopperBlur: true,
124
+ onMount: (instance) => { ... },
125
+ onHidden: (instance) => { ... }
126
+ });
127
+ ```
128
+
129
+ ### Registro de componentes en desy-html.js:
130
+
131
+ Cada componente exporta una funcion que:
132
+ 1. Llama a la funcion del modulo ARIA pasando `aria`
133
+ 2. Busca elementos DOM con `querySelectorAll('[data-module="c-xxx"]')`
134
+ 3. Inicializa instancias
135
+
136
+ Ejemplo (listboxComponent, linea 114):
137
+ ```js
138
+ export function listboxComponent(aria) {
139
+ listbox(aria);
140
+ const modules = document.querySelectorAll('[data-module]');
141
+ for (const item in modules) if (modules.hasOwnProperty(item)) {
142
+ const moduleValue = modules[item].getAttribute('data-module');
143
+ if (moduleValue == 'c-listbox') {
144
+ // Buscar sub-elementos, crear instancias, registrar eventos
145
+ }
146
+ }
147
+ }
148
+ ```
149
+
150
+ ## Especificacion tecnica
151
+
152
+ ### Archivo: `src/js/aria/combobox.js`
153
+
154
+ ```js
155
+ export function combobox(aria) {
156
+ /**
157
+ * @constructor
158
+ * @desc Combobox with autocomplete (list/both modes)
159
+ * @param {HTMLElement} comboboxNode - The wrapper element [data-module="c-combobox"]
160
+ */
161
+ aria.Combobox = function (comboboxNode) {
162
+ this.comboboxNode = comboboxNode;
163
+ this.input = comboboxNode.querySelector('[data-module="c-combobox-input"]');
164
+ this.clearButton = comboboxNode.querySelector('[data-module="c-combobox-clear"]');
165
+ this.hiddenInput = comboboxNode.querySelector('[data-module="c-combobox-hidden"]');
166
+ this.tooltip = comboboxNode.querySelector('[data-module="c-combobox-tooltip"]');
167
+ this.listbox = comboboxNode.querySelector('[data-module="c-combobox-list"]');
168
+ this.noResults = comboboxNode.querySelector('[data-module="c-combobox-no-results"]');
169
+
170
+ this.options = [];
171
+ this.filteredOptions = [];
172
+ this.activeIndex = -1;
173
+ this.isOpen = false;
174
+ this.autocompleteMode = this.input.getAttribute('aria-autocomplete') || 'both';
175
+ this.floatingInstance = null;
176
+
177
+ this.init();
178
+ };
179
+
180
+ // Metodo principal: inicializacion
181
+ // - Cachear todas las opciones (texto + elemento)
182
+ // - Registrar eventos de teclado, input, blur, click
183
+ // - Inicializar floating element
184
+ // - Configurar clear button
185
+ // - Configurar scroll listener
186
+ aria.Combobox.prototype.init = function () { ... };
187
+
188
+ // Cachear opciones del DOM
189
+ aria.Combobox.prototype.cacheOptions = function () {
190
+ var optionElements = this.listbox.querySelectorAll('[role="option"]');
191
+ this.options = [];
192
+ for (var i = 0; i < optionElements.length; i++) {
193
+ this.options.push({
194
+ element: optionElements[i],
195
+ text: optionElements[i].textContent.trim(),
196
+ normalized: this.normalize(optionElements[i].textContent.trim())
197
+ });
198
+ }
199
+ this.filteredOptions = this.options.slice();
200
+ };
201
+
202
+ // Normalizar texto (quitar acentos para filtrado)
203
+ aria.Combobox.prototype.normalize = function (str) {
204
+ return str.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase();
205
+ };
206
+
207
+ // Filtrar opciones (starts-with)
208
+ aria.Combobox.prototype.filterOptions = function (query) {
209
+ var normalizedQuery = this.normalize(query);
210
+ this.filteredOptions = [];
211
+
212
+ for (var i = 0; i < this.options.length; i++) {
213
+ if (this.options[i].normalized.indexOf(normalizedQuery) === 0) {
214
+ this.options[i].element.style.display = '';
215
+ this.options[i].element.removeAttribute('hidden');
216
+ this.filteredOptions.push(this.options[i]);
217
+ } else {
218
+ this.options[i].element.style.display = 'none';
219
+ this.options[i].element.setAttribute('hidden', '');
220
+ }
221
+ }
222
+
223
+ // Mostrar/ocultar mensaje "sin resultados"
224
+ if (this.filteredOptions.length === 0) {
225
+ this.noResults.classList.remove('hidden');
226
+ } else {
227
+ this.noResults.classList.add('hidden');
228
+ }
229
+ };
230
+
231
+ // Abrir popup
232
+ aria.Combobox.prototype.open = function () {
233
+ if (this.isOpen) return;
234
+ this.isOpen = true;
235
+ this.input.setAttribute('aria-expanded', 'true');
236
+
237
+ if (this.floatingInstance) {
238
+ this.floatingInstance.show();
239
+ } else {
240
+ this.tooltip.classList.remove('hidden');
241
+ }
242
+
243
+ this.comboboxNode.dispatchEvent(new CustomEvent('combobox:open', { bubbles: true }));
244
+ };
245
+
246
+ // Cerrar popup
247
+ aria.Combobox.prototype.close = function () {
248
+ if (!this.isOpen) return;
249
+ this.isOpen = false;
250
+ this.activeIndex = -1;
251
+ this.input.setAttribute('aria-expanded', 'false');
252
+ this.input.removeAttribute('aria-activedescendant');
253
+ this.clearActiveDescendant();
254
+
255
+ if (this.floatingInstance) {
256
+ this.floatingInstance.hide();
257
+ } else {
258
+ this.tooltip.classList.add('hidden');
259
+ }
260
+
261
+ this.comboboxNode.dispatchEvent(new CustomEvent('combobox:close', { bubbles: true }));
262
+ };
263
+
264
+ // Focus visual en opcion (aria-activedescendant)
265
+ aria.Combobox.prototype.setActiveDescendant = function (index) {
266
+ this.clearActiveDescendant();
267
+
268
+ if (index < 0 || index >= this.filteredOptions.length) return;
269
+
270
+ this.activeIndex = index;
271
+ var option = this.filteredOptions[index].element;
272
+ option.classList.add('focused');
273
+ this.input.setAttribute('aria-activedescendant', option.id);
274
+
275
+ // Scroll into view
276
+ this.scrollOptionIntoView(option);
277
+ };
278
+
279
+ // Limpiar focus visual
280
+ aria.Combobox.prototype.clearActiveDescendant = function () {
281
+ var focused = this.listbox.querySelector('.focused');
282
+ if (focused) {
283
+ focused.classList.remove('focused');
284
+ }
285
+ this.input.removeAttribute('aria-activedescendant');
286
+ };
287
+
288
+ // Scroll opcion visible dentro del listbox
289
+ aria.Combobox.prototype.scrollOptionIntoView = function (option) {
290
+ var listboxEl = this.listbox;
291
+ if (listboxEl.scrollHeight <= listboxEl.clientHeight) return;
292
+
293
+ var scrollBottom = listboxEl.clientHeight + listboxEl.scrollTop;
294
+ var elementBottom = option.offsetTop + option.offsetHeight;
295
+
296
+ if (elementBottom > scrollBottom) {
297
+ listboxEl.scrollTop = elementBottom - listboxEl.clientHeight;
298
+ } else if (option.offsetTop < listboxEl.scrollTop) {
299
+ listboxEl.scrollTop = option.offsetTop;
300
+ }
301
+ };
302
+
303
+ // Seleccionar opcion
304
+ aria.Combobox.prototype.selectOption = function (index) {
305
+ if (index < 0 || index >= this.filteredOptions.length) return;
306
+
307
+ var option = this.filteredOptions[index];
308
+
309
+ // Limpiar seleccion previa
310
+ for (var i = 0; i < this.options.length; i++) {
311
+ this.options[i].element.setAttribute('aria-selected', 'false');
312
+ }
313
+
314
+ // Seleccionar nueva opcion
315
+ option.element.setAttribute('aria-selected', 'true');
316
+
317
+ // Actualizar input visible y hidden
318
+ this.input.value = option.text;
319
+ this.hiddenInput.value = option.text;
320
+
321
+ // Mostrar clear button
322
+ this.updateClearButton();
323
+
324
+ // Cerrar popup
325
+ this.close();
326
+
327
+ // Foco al input
328
+ this.input.focus();
329
+
330
+ // Dispatch evento
331
+ this.comboboxNode.dispatchEvent(new CustomEvent('combobox:select', {
332
+ bubbles: true,
333
+ detail: { value: option.text, element: option.element }
334
+ }));
335
+ };
336
+
337
+ // Click en opcion
338
+ aria.Combobox.prototype.handleOptionClick = function (evt) {
339
+ var optionEl = evt.target.closest('[role="option"]');
340
+ if (!optionEl) return;
341
+
342
+ var index = this.filteredOptions.findIndex(function (opt) {
343
+ return opt.element === optionEl;
344
+ });
345
+
346
+ if (index >= 0) {
347
+ this.selectOption(index);
348
+ }
349
+ };
350
+
351
+ // Input event (filtrando mientras escribe)
352
+ aria.Combobox.prototype.handleInput = function () {
353
+ var value = this.input.value;
354
+
355
+ this.filterOptions(value);
356
+ this.updateClearButton();
357
+
358
+ if (value.length > 0) {
359
+ this.open();
360
+ this.activeIndex = -1;
361
+
362
+ // Inline completion (modo "both")
363
+ if (this.autocompleteMode === 'both' && this.filteredOptions.length > 0) {
364
+ var firstMatch = this.filteredOptions[0].text;
365
+ var inputLength = value.length;
366
+ this.input.value = firstMatch;
367
+ this.input.setSelectionRange(inputLength, firstMatch.length);
368
+ this.setActiveDescendant(0);
369
+ }
370
+ } else {
371
+ this.close();
372
+ this.hiddenInput.value = '';
373
+ }
374
+ };
375
+
376
+ // Keyboard handler
377
+ aria.Combobox.prototype.handleKeyDown = function (evt) {
378
+ var key = evt.key;
379
+
380
+ switch (key) {
381
+ case 'ArrowDown':
382
+ evt.preventDefault();
383
+ if (!this.isOpen) {
384
+ this.filterOptions(this.input.value);
385
+ this.open();
386
+ }
387
+ if (this.activeIndex < this.filteredOptions.length - 1) {
388
+ this.setActiveDescendant(this.activeIndex + 1);
389
+ }
390
+ break;
391
+
392
+ case 'ArrowUp':
393
+ evt.preventDefault();
394
+ if (!this.isOpen) {
395
+ this.filterOptions(this.input.value);
396
+ this.open();
397
+ }
398
+ if (this.activeIndex > 0) {
399
+ this.setActiveDescendant(this.activeIndex - 1);
400
+ }
401
+ break;
402
+
403
+ case 'Enter':
404
+ if (this.isOpen && this.activeIndex >= 0) {
405
+ evt.preventDefault();
406
+ this.selectOption(this.activeIndex);
407
+ }
408
+ break;
409
+
410
+ case 'Escape':
411
+ if (this.isOpen) {
412
+ evt.preventDefault();
413
+ this.close();
414
+ this.input.focus();
415
+ }
416
+ break;
417
+
418
+ case 'Home':
419
+ if (this.isOpen && this.filteredOptions.length > 0) {
420
+ evt.preventDefault();
421
+ this.setActiveDescendant(0);
422
+ }
423
+ break;
424
+
425
+ case 'End':
426
+ if (this.isOpen && this.filteredOptions.length > 0) {
427
+ evt.preventDefault();
428
+ this.setActiveDescendant(this.filteredOptions.length - 1);
429
+ }
430
+ break;
431
+
432
+ default:
433
+ break;
434
+ }
435
+ };
436
+
437
+ // Blur handler
438
+ aria.Combobox.prototype.handleBlur = function () {
439
+ // Usar setTimeout para permitir clicks en opciones
440
+ var self = this;
441
+ setTimeout(function () {
442
+ if (!self.comboboxNode.contains(document.activeElement)) {
443
+ // Validar valor: si no coincide con ninguna opcion, limpiar
444
+ var currentValue = self.input.value.trim();
445
+ var matchFound = false;
446
+
447
+ for (var i = 0; i < self.options.length; i++) {
448
+ if (self.options[i].text === currentValue) {
449
+ matchFound = true;
450
+ break;
451
+ }
452
+ }
453
+
454
+ if (!matchFound) {
455
+ self.clearValue();
456
+ }
457
+
458
+ self.close();
459
+ }
460
+ }, 200);
461
+ };
462
+
463
+ // Limpiar valor
464
+ aria.Combobox.prototype.clearValue = function () {
465
+ this.input.value = '';
466
+ this.hiddenInput.value = '';
467
+
468
+ for (var i = 0; i < this.options.length; i++) {
469
+ this.options[i].element.setAttribute('aria-selected', 'false');
470
+ }
471
+
472
+ this.updateClearButton();
473
+ this.comboboxNode.dispatchEvent(new CustomEvent('combobox:clear', { bubbles: true }));
474
+ };
475
+
476
+ // Clear button handler
477
+ aria.Combobox.prototype.handleClear = function () {
478
+ this.clearValue();
479
+ this.close();
480
+ this.filterOptions('');
481
+ this.input.focus();
482
+ };
483
+
484
+ // Mostrar/ocultar clear button
485
+ aria.Combobox.prototype.updateClearButton = function () {
486
+ if (this.input.value.length > 0) {
487
+ this.clearButton.removeAttribute('hidden');
488
+ } else {
489
+ this.clearButton.setAttribute('hidden', '');
490
+ }
491
+ };
492
+
493
+ // Scroll de pagina cierra popup
494
+ aria.Combobox.prototype.handleScroll = function () {
495
+ if (this.isOpen) {
496
+ this.close();
497
+ }
498
+ };
499
+
500
+ return aria;
501
+ }
502
+ ```
503
+
504
+ ### Detalle: metodo init completo
505
+
506
+ ```js
507
+ aria.Combobox.prototype.init = function () {
508
+ this.cacheOptions();
509
+ this.updateClearButton();
510
+
511
+ // Event listeners
512
+ this.input.addEventListener('input', this.handleInput.bind(this));
513
+ this.input.addEventListener('keydown', this.handleKeyDown.bind(this));
514
+ this.input.addEventListener('blur', this.handleBlur.bind(this));
515
+
516
+ // Click en opciones
517
+ this.listbox.addEventListener('click', this.handleOptionClick.bind(this));
518
+
519
+ // Prevenir que click en listbox cierre el popup via blur
520
+ this.listbox.addEventListener('mousedown', function (evt) {
521
+ evt.preventDefault();
522
+ });
523
+
524
+ // Clear button
525
+ this.clearButton.addEventListener('click', this.handleClear.bind(this));
526
+
527
+ // Scroll cierra popup
528
+ window.addEventListener('scroll', this.handleScroll.bind(this), true);
529
+
530
+ // Si hay valor por defecto, marcar la opcion correspondiente
531
+ if (this.input.value) {
532
+ for (var i = 0; i < this.options.length; i++) {
533
+ if (this.options[i].text === this.input.value) {
534
+ this.options[i].element.setAttribute('aria-selected', 'true');
535
+ this.hiddenInput.value = this.input.value;
536
+ break;
537
+ }
538
+ }
539
+ }
540
+ };
541
+ ```
542
+
543
+ ### Notas importantes de implementacion
544
+
545
+ 1. **No usar KeyCode numerico**: El patron listbox.js usa `aria.KeyCode.UP` etc, pero para combobox usar `evt.key` strings ('ArrowDown', 'ArrowUp', etc.) que es mas moderno y legible.
546
+
547
+ 2. **setSelectionRange para inline completion**: Solo en modo "both". Despues de escribir, el input se completa con el primer match y se selecciona la parte autocompletada para que el usuario pueda seguir escribiendo.
548
+
549
+ 3. **mousedown preventDefault en listbox**: Critico para que el click en opciones funcione. Sin esto, el blur del input se dispara antes del click y cierra el popup.
550
+
551
+ 4. **scroll listener con capture**: `window.addEventListener('scroll', handler, true)` con capture para detectar scroll en cualquier contenedor padre.
552
+
553
+ 5. **setTimeout en blur**: Necesario (200ms) para que el click en opciones se procese antes de validar el blur.
554
+
555
+ 6. **createFloatingElement**: Se configurara en la Fase 4 (integracion). El modulo ARIA debe funcionar con fallback (classList toggle) si no hay floating instance.
556
+
557
+ 7. **Normalizacion de acentos**: `str.normalize('NFD').replace(/[\u0300-\u036f]/g, '')` convierte "aragon" y "Aragon" al mismo resultado para filtrado.
558
+
559
+ ## Criterios de aceptacion
560
+
561
+ - [ ] Archivo `src/js/aria/combobox.js` creado
562
+ - [ ] Exporta funcion `combobox(aria)` que anade `aria.Combobox` constructor
563
+ - [ ] Usa patron de prototipos (consistente con listbox.js)
564
+ - [ ] Constructor recibe el elemento wrapper `[data-module="c-combobox"]`
565
+ - [ ] Localiza sub-elementos via `data-module` queries
566
+ - [ ] **Filtrado**: starts-with con normalizacion de acentos (NFD + regex)
567
+ - [ ] **Modo list**: filtra opciones sin inline completion
568
+ - [ ] **Modo both**: filtra + inline completion con `setSelectionRange()`
569
+ - [ ] **Teclado**:
570
+ - [ ] ArrowDown: abre popup si cerrado, mueve focus abajo
571
+ - [ ] ArrowUp: abre popup si cerrado, mueve focus arriba
572
+ - [ ] Enter: selecciona opcion activa
573
+ - [ ] Escape: cierra popup, foco al input
574
+ - [ ] Home: primera opcion filtrada
575
+ - [ ] End: ultima opcion filtrada
576
+ - [ ] **Focus**: usa `aria-activedescendant` (no mueve focus real del input)
577
+ - [ ] **Scroll into view**: opcion activa siempre visible en el listbox
578
+ - [ ] **Blur sin match**: limpia input y hidden value
579
+ - [ ] **Clear button**: limpia todo, cierra popup, foco al input, dispara combobox:clear
580
+ - [ ] **Clear button visibility**: visible cuando hay texto, hidden cuando vacio
581
+ - [ ] **Seleccion**: actualiza input visible, hidden input, aria-selected, dispara combobox:select
582
+ - [ ] **Post-seleccion**: cierra popup y devuelve foco al input
583
+ - [ ] **Scroll pagina**: cierra popup
584
+ - [ ] **Eventos custom**: combobox:select, combobox:clear, combobox:open, combobox:close con bubbles:true
585
+ - [ ] **Sin resultados**: muestra/oculta mensaje segun filtrado
586
+ - [ ] **Valor por defecto**: si input tiene value, marca la opcion correspondiente
587
+ - [ ] **mousedown preventDefault**: en listbox para evitar blur prematuro
588
+ - [ ] No depende de createFloatingElement (funciona con fallback classList)
589
+ ```