@furystack/shades-common-components 13.5.0 → 15.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 (540) hide show
  1. package/CHANGELOG.md +109 -0
  2. package/README.md +3 -3
  3. package/esm/components/accordion/accordion-item.d.ts.map +1 -1
  4. package/esm/components/accordion/accordion-item.js +7 -10
  5. package/esm/components/accordion/accordion-item.js.map +1 -1
  6. package/esm/components/accordion/accordion.d.ts +7 -0
  7. package/esm/components/accordion/accordion.d.ts.map +1 -1
  8. package/esm/components/accordion/accordion.js +5 -2
  9. package/esm/components/accordion/accordion.js.map +1 -1
  10. package/esm/components/accordion/accordion.spec.js +91 -50
  11. package/esm/components/accordion/accordion.spec.js.map +1 -1
  12. package/esm/components/alert.js +1 -1
  13. package/esm/components/alert.js.map +1 -1
  14. package/esm/components/app-bar-link.js +1 -1
  15. package/esm/components/app-bar-link.js.map +1 -1
  16. package/esm/components/app-bar.js +1 -1
  17. package/esm/components/app-bar.js.map +1 -1
  18. package/esm/components/avatar.js +1 -1
  19. package/esm/components/avatar.js.map +1 -1
  20. package/esm/components/badge.js +1 -1
  21. package/esm/components/badge.js.map +1 -1
  22. package/esm/components/breadcrumb.js +1 -1
  23. package/esm/components/breadcrumb.js.map +1 -1
  24. package/esm/components/breadcrumb.spec.js +3 -3
  25. package/esm/components/breadcrumb.spec.js.map +1 -1
  26. package/esm/components/button-group.js +4 -4
  27. package/esm/components/button-group.js.map +1 -1
  28. package/esm/components/button.js +1 -1
  29. package/esm/components/button.js.map +1 -1
  30. package/esm/components/button.spec.js +1 -1
  31. package/esm/components/button.spec.js.map +1 -1
  32. package/esm/components/cache-view.js +1 -1
  33. package/esm/components/cache-view.js.map +1 -1
  34. package/esm/components/cache-view.spec.js +2 -2
  35. package/esm/components/cache-view.spec.js.map +1 -1
  36. package/esm/components/card.js +5 -5
  37. package/esm/components/card.js.map +1 -1
  38. package/esm/components/carousel.js +2 -2
  39. package/esm/components/carousel.js.map +1 -1
  40. package/esm/components/chip.d.ts.map +1 -1
  41. package/esm/components/chip.js +5 -3
  42. package/esm/components/chip.js.map +1 -1
  43. package/esm/components/chip.spec.js +42 -0
  44. package/esm/components/chip.spec.js.map +1 -1
  45. package/esm/components/circular-progress.d.ts +2 -4
  46. package/esm/components/circular-progress.d.ts.map +1 -1
  47. package/esm/components/circular-progress.js +3 -6
  48. package/esm/components/circular-progress.js.map +1 -1
  49. package/esm/components/circular-progress.spec.js +19 -14
  50. package/esm/components/circular-progress.spec.js.map +1 -1
  51. package/esm/components/command-palette/command-palette-input.js +1 -1
  52. package/esm/components/command-palette/command-palette-input.js.map +1 -1
  53. package/esm/components/command-palette/command-palette-suggestion-list.js +1 -1
  54. package/esm/components/command-palette/command-palette-suggestion-list.js.map +1 -1
  55. package/esm/components/command-palette/command-palette-suggestion-list.spec.js +1 -1
  56. package/esm/components/command-palette/command-palette-suggestion-list.spec.js.map +1 -1
  57. package/esm/components/command-palette/index.d.ts.map +1 -1
  58. package/esm/components/command-palette/index.js +15 -2
  59. package/esm/components/command-palette/index.js.map +1 -1
  60. package/esm/components/command-palette/index.spec.js +78 -33
  61. package/esm/components/command-palette/index.spec.js.map +1 -1
  62. package/esm/components/context-menu/context-menu-item.js +1 -1
  63. package/esm/components/context-menu/context-menu-item.js.map +1 -1
  64. package/esm/components/context-menu/context-menu.js +1 -1
  65. package/esm/components/context-menu/context-menu.js.map +1 -1
  66. package/esm/components/data-grid/body.js +1 -1
  67. package/esm/components/data-grid/body.js.map +1 -1
  68. package/esm/components/data-grid/data-grid-row.d.ts.map +1 -1
  69. package/esm/components/data-grid/data-grid-row.js +19 -3
  70. package/esm/components/data-grid/data-grid-row.js.map +1 -1
  71. package/esm/components/data-grid/data-grid.d.ts +12 -2
  72. package/esm/components/data-grid/data-grid.d.ts.map +1 -1
  73. package/esm/components/data-grid/data-grid.js +33 -13
  74. package/esm/components/data-grid/data-grid.js.map +1 -1
  75. package/esm/components/data-grid/data-grid.spec.js +170 -90
  76. package/esm/components/data-grid/data-grid.spec.js.map +1 -1
  77. package/esm/components/data-grid/filters/boolean-filter.d.ts +2 -2
  78. package/esm/components/data-grid/filters/boolean-filter.d.ts.map +1 -1
  79. package/esm/components/data-grid/filters/boolean-filter.js +4 -4
  80. package/esm/components/data-grid/filters/boolean-filter.js.map +1 -1
  81. package/esm/components/data-grid/filters/boolean-filter.spec.js +18 -17
  82. package/esm/components/data-grid/filters/boolean-filter.spec.js.map +1 -1
  83. package/esm/components/data-grid/filters/date-filter.d.ts +2 -2
  84. package/esm/components/data-grid/filters/date-filter.d.ts.map +1 -1
  85. package/esm/components/data-grid/filters/date-filter.js +6 -6
  86. package/esm/components/data-grid/filters/date-filter.js.map +1 -1
  87. package/esm/components/data-grid/filters/date-filter.spec.js +26 -21
  88. package/esm/components/data-grid/filters/date-filter.spec.js.map +1 -1
  89. package/esm/components/data-grid/filters/enum-filter.d.ts +2 -2
  90. package/esm/components/data-grid/filters/enum-filter.d.ts.map +1 -1
  91. package/esm/components/data-grid/filters/enum-filter.js +5 -5
  92. package/esm/components/data-grid/filters/enum-filter.js.map +1 -1
  93. package/esm/components/data-grid/filters/enum-filter.spec.js +21 -19
  94. package/esm/components/data-grid/filters/enum-filter.spec.js.map +1 -1
  95. package/esm/components/data-grid/filters/filter-dropdown.js +1 -1
  96. package/esm/components/data-grid/filters/filter-dropdown.js.map +1 -1
  97. package/esm/components/data-grid/filters/number-filter.d.ts +2 -2
  98. package/esm/components/data-grid/filters/number-filter.d.ts.map +1 -1
  99. package/esm/components/data-grid/filters/number-filter.js +5 -5
  100. package/esm/components/data-grid/filters/number-filter.js.map +1 -1
  101. package/esm/components/data-grid/filters/number-filter.spec.js +23 -21
  102. package/esm/components/data-grid/filters/number-filter.spec.js.map +1 -1
  103. package/esm/components/data-grid/filters/string-filter.d.ts +2 -2
  104. package/esm/components/data-grid/filters/string-filter.d.ts.map +1 -1
  105. package/esm/components/data-grid/filters/string-filter.js +5 -5
  106. package/esm/components/data-grid/filters/string-filter.js.map +1 -1
  107. package/esm/components/data-grid/filters/string-filter.spec.js +21 -19
  108. package/esm/components/data-grid/filters/string-filter.spec.js.map +1 -1
  109. package/esm/components/data-grid/footer.d.ts +2 -2
  110. package/esm/components/data-grid/footer.d.ts.map +1 -1
  111. package/esm/components/data-grid/footer.js +8 -13
  112. package/esm/components/data-grid/footer.js.map +1 -1
  113. package/esm/components/data-grid/footer.spec.js +38 -27
  114. package/esm/components/data-grid/footer.spec.js.map +1 -1
  115. package/esm/components/data-grid/header.d.ts +6 -6
  116. package/esm/components/data-grid/header.d.ts.map +1 -1
  117. package/esm/components/data-grid/header.js +16 -17
  118. package/esm/components/data-grid/header.js.map +1 -1
  119. package/esm/components/data-grid/header.spec.js +66 -60
  120. package/esm/components/data-grid/header.spec.js.map +1 -1
  121. package/esm/components/data-grid/selection-cell.d.ts.map +1 -1
  122. package/esm/components/data-grid/selection-cell.js +2 -2
  123. package/esm/components/data-grid/selection-cell.js.map +1 -1
  124. package/esm/components/dialog.d.ts +11 -0
  125. package/esm/components/dialog.d.ts.map +1 -1
  126. package/esm/components/dialog.js +3 -3
  127. package/esm/components/dialog.js.map +1 -1
  128. package/esm/components/dialog.spec.js +54 -2
  129. package/esm/components/dialog.spec.js.map +1 -1
  130. package/esm/components/divider.js +1 -1
  131. package/esm/components/divider.js.map +1 -1
  132. package/esm/components/drawer/drawer-toggle-button.js +1 -1
  133. package/esm/components/drawer/drawer-toggle-button.js.map +1 -1
  134. package/esm/components/drawer/index.js +1 -1
  135. package/esm/components/drawer/index.js.map +1 -1
  136. package/esm/components/dropdown.d.ts.map +1 -1
  137. package/esm/components/dropdown.js +2 -2
  138. package/esm/components/dropdown.js.map +1 -1
  139. package/esm/components/dropdown.spec.js +8 -0
  140. package/esm/components/dropdown.spec.js.map +1 -1
  141. package/esm/components/fab.js +1 -1
  142. package/esm/components/fab.js.map +1 -1
  143. package/esm/components/form.js +1 -1
  144. package/esm/components/form.js.map +1 -1
  145. package/esm/components/grid.js +1 -1
  146. package/esm/components/grid.js.map +1 -1
  147. package/esm/components/icons/icon.js +1 -1
  148. package/esm/components/icons/icon.js.map +1 -1
  149. package/esm/components/image.d.ts.map +1 -1
  150. package/esm/components/image.js +17 -8
  151. package/esm/components/image.js.map +1 -1
  152. package/esm/components/image.spec.js +60 -0
  153. package/esm/components/image.spec.js.map +1 -1
  154. package/esm/components/inputs/autocomplete.js +1 -1
  155. package/esm/components/inputs/autocomplete.js.map +1 -1
  156. package/esm/components/inputs/checkbox.d.ts.map +1 -1
  157. package/esm/components/inputs/checkbox.js +2 -1
  158. package/esm/components/inputs/checkbox.js.map +1 -1
  159. package/esm/components/inputs/checkbox.spec.js +1 -1
  160. package/esm/components/inputs/checkbox.spec.js.map +1 -1
  161. package/esm/components/inputs/input-number.js +1 -1
  162. package/esm/components/inputs/input-number.js.map +1 -1
  163. package/esm/components/inputs/input-number.spec.js +1 -1
  164. package/esm/components/inputs/input-number.spec.js.map +1 -1
  165. package/esm/components/inputs/input.js +1 -1
  166. package/esm/components/inputs/input.js.map +1 -1
  167. package/esm/components/inputs/input.spec.js +1 -1
  168. package/esm/components/inputs/input.spec.js.map +1 -1
  169. package/esm/components/inputs/radio-group.js +1 -1
  170. package/esm/components/inputs/radio-group.js.map +1 -1
  171. package/esm/components/inputs/radio-group.spec.js +1 -1
  172. package/esm/components/inputs/radio-group.spec.js.map +1 -1
  173. package/esm/components/inputs/radio.d.ts.map +1 -1
  174. package/esm/components/inputs/radio.js +2 -1
  175. package/esm/components/inputs/radio.js.map +1 -1
  176. package/esm/components/inputs/radio.spec.js +1 -1
  177. package/esm/components/inputs/radio.spec.js.map +1 -1
  178. package/esm/components/inputs/select.js +1 -1
  179. package/esm/components/inputs/select.js.map +1 -1
  180. package/esm/components/inputs/slider.d.ts.map +1 -1
  181. package/esm/components/inputs/slider.js +2 -1
  182. package/esm/components/inputs/slider.js.map +1 -1
  183. package/esm/components/inputs/switch.d.ts.map +1 -1
  184. package/esm/components/inputs/switch.js +2 -1
  185. package/esm/components/inputs/switch.js.map +1 -1
  186. package/esm/components/inputs/switch.spec.js +1 -1
  187. package/esm/components/inputs/switch.spec.js.map +1 -1
  188. package/esm/components/inputs/text-area.js +1 -1
  189. package/esm/components/inputs/text-area.js.map +1 -1
  190. package/esm/components/inputs/text-area.spec.js +1 -1
  191. package/esm/components/inputs/text-area.spec.js.map +1 -1
  192. package/esm/components/linear-progress.d.ts +2 -4
  193. package/esm/components/linear-progress.d.ts.map +1 -1
  194. package/esm/components/linear-progress.js +3 -6
  195. package/esm/components/linear-progress.js.map +1 -1
  196. package/esm/components/linear-progress.spec.js +21 -18
  197. package/esm/components/linear-progress.spec.js.map +1 -1
  198. package/esm/components/list/list-item.d.ts.map +1 -1
  199. package/esm/components/list/list-item.js +22 -6
  200. package/esm/components/list/list-item.js.map +1 -1
  201. package/esm/components/list/list.d.ts +7 -0
  202. package/esm/components/list/list.d.ts.map +1 -1
  203. package/esm/components/list/list.js +29 -9
  204. package/esm/components/list/list.js.map +1 -1
  205. package/esm/components/list/list.spec.js +117 -23
  206. package/esm/components/list/list.spec.js.map +1 -1
  207. package/esm/components/loader.js +1 -1
  208. package/esm/components/loader.js.map +1 -1
  209. package/esm/components/loader.spec.js +1 -1
  210. package/esm/components/loader.spec.js.map +1 -1
  211. package/esm/components/markdown/markdown-display.d.ts.map +1 -1
  212. package/esm/components/markdown/markdown-display.js +12 -2
  213. package/esm/components/markdown/markdown-display.js.map +1 -1
  214. package/esm/components/markdown/markdown-display.spec.js +98 -1
  215. package/esm/components/markdown/markdown-display.spec.js.map +1 -1
  216. package/esm/components/markdown/markdown-editor.js +1 -1
  217. package/esm/components/markdown/markdown-editor.js.map +1 -1
  218. package/esm/components/markdown/markdown-editor.spec.js +88 -1
  219. package/esm/components/markdown/markdown-editor.spec.js.map +1 -1
  220. package/esm/components/markdown/markdown-input.js +1 -1
  221. package/esm/components/markdown/markdown-input.js.map +1 -1
  222. package/esm/components/markdown/markdown-input.spec.js +1 -1
  223. package/esm/components/markdown/markdown-input.spec.js.map +1 -1
  224. package/esm/components/menu/menu.js +2 -2
  225. package/esm/components/menu/menu.js.map +1 -1
  226. package/esm/components/modal.d.ts +10 -0
  227. package/esm/components/modal.d.ts.map +1 -1
  228. package/esm/components/modal.js +25 -5
  229. package/esm/components/modal.js.map +1 -1
  230. package/esm/components/modal.spec.js +89 -4
  231. package/esm/components/modal.spec.js.map +1 -1
  232. package/esm/components/noty-list.js +2 -2
  233. package/esm/components/noty-list.js.map +1 -1
  234. package/esm/components/page-container/index.js +1 -1
  235. package/esm/components/page-container/index.js.map +1 -1
  236. package/esm/components/page-container/page-header.js +1 -1
  237. package/esm/components/page-container/page-header.js.map +1 -1
  238. package/esm/components/page-layout/index.js +2 -2
  239. package/esm/components/page-layout/index.js.map +1 -1
  240. package/esm/components/page-layout/index.spec.js +14 -0
  241. package/esm/components/page-layout/index.spec.js.map +1 -1
  242. package/esm/components/pagination.js +1 -1
  243. package/esm/components/pagination.js.map +1 -1
  244. package/esm/components/paper.js +1 -1
  245. package/esm/components/paper.js.map +1 -1
  246. package/esm/components/rating.d.ts.map +1 -1
  247. package/esm/components/rating.js +29 -22
  248. package/esm/components/rating.js.map +1 -1
  249. package/esm/components/rating.spec.js +152 -5
  250. package/esm/components/rating.spec.js.map +1 -1
  251. package/esm/components/result.js +1 -1
  252. package/esm/components/result.js.map +1 -1
  253. package/esm/components/skeleton.js +1 -1
  254. package/esm/components/skeleton.js.map +1 -1
  255. package/esm/components/suggest/index.d.ts.map +1 -1
  256. package/esm/components/suggest/index.js +15 -2
  257. package/esm/components/suggest/index.js.map +1 -1
  258. package/esm/components/suggest/index.spec.js +99 -44
  259. package/esm/components/suggest/index.spec.js.map +1 -1
  260. package/esm/components/suggest/suggest-input.js +1 -1
  261. package/esm/components/suggest/suggest-input.js.map +1 -1
  262. package/esm/components/suggest/suggest-input.spec.js +1 -1
  263. package/esm/components/suggest/suggest-input.spec.js.map +1 -1
  264. package/esm/components/suggest/suggestion-list.js +1 -1
  265. package/esm/components/suggest/suggestion-list.js.map +1 -1
  266. package/esm/components/suggest/suggestion-list.spec.js +1 -1
  267. package/esm/components/suggest/suggestion-list.spec.js.map +1 -1
  268. package/esm/components/tabs.d.ts.map +1 -1
  269. package/esm/components/tabs.js +6 -2
  270. package/esm/components/tabs.js.map +1 -1
  271. package/esm/components/timeline.js +2 -2
  272. package/esm/components/timeline.js.map +1 -1
  273. package/esm/components/tooltip.js +1 -1
  274. package/esm/components/tooltip.js.map +1 -1
  275. package/esm/components/tree/tree-item.d.ts.map +1 -1
  276. package/esm/components/tree/tree-item.js +19 -6
  277. package/esm/components/tree/tree-item.js.map +1 -1
  278. package/esm/components/tree/tree.d.ts +7 -0
  279. package/esm/components/tree/tree.d.ts.map +1 -1
  280. package/esm/components/tree/tree.js +13 -4
  281. package/esm/components/tree/tree.js.map +1 -1
  282. package/esm/components/tree/tree.spec.js +64 -2
  283. package/esm/components/tree/tree.spec.js.map +1 -1
  284. package/esm/components/typography.js +1 -1
  285. package/esm/components/typography.js.map +1 -1
  286. package/esm/components/wizard/index.js +1 -1
  287. package/esm/components/wizard/index.js.map +1 -1
  288. package/esm/components/wizard/index.spec.js +3 -3
  289. package/esm/components/wizard/index.spec.js.map +1 -1
  290. package/esm/services/collection-service.d.ts +9 -0
  291. package/esm/services/collection-service.d.ts.map +1 -1
  292. package/esm/services/collection-service.js +33 -11
  293. package/esm/services/collection-service.js.map +1 -1
  294. package/esm/services/collection-service.spec.js +33 -24
  295. package/esm/services/collection-service.spec.js.map +1 -1
  296. package/esm/services/css-variable-theme.d.ts +7 -0
  297. package/esm/services/css-variable-theme.d.ts.map +1 -1
  298. package/esm/services/css-variable-theme.js +23 -0
  299. package/esm/services/css-variable-theme.js.map +1 -1
  300. package/esm/services/css-variable-theme.spec.js +1 -0
  301. package/esm/services/css-variable-theme.spec.js.map +1 -1
  302. package/esm/services/list-service.d.ts +9 -0
  303. package/esm/services/list-service.d.ts.map +1 -1
  304. package/esm/services/list-service.js +13 -13
  305. package/esm/services/list-service.js.map +1 -1
  306. package/esm/services/list-service.spec.js +13 -33
  307. package/esm/services/list-service.spec.js.map +1 -1
  308. package/esm/services/theme-provider-service.d.ts +3 -0
  309. package/esm/services/theme-provider-service.d.ts.map +1 -1
  310. package/esm/services/theme-provider-service.js.map +1 -1
  311. package/esm/services/tree-service.d.ts.map +1 -1
  312. package/esm/services/tree-service.js +5 -9
  313. package/esm/services/tree-service.js.map +1 -1
  314. package/esm/services/tree-service.spec.js +12 -9
  315. package/esm/services/tree-service.spec.js.map +1 -1
  316. package/esm/themes/architect-theme.d.ts +1 -0
  317. package/esm/themes/architect-theme.d.ts.map +1 -1
  318. package/esm/themes/architect-theme.js +1 -0
  319. package/esm/themes/architect-theme.js.map +1 -1
  320. package/esm/themes/auditore-theme.d.ts +1 -0
  321. package/esm/themes/auditore-theme.d.ts.map +1 -1
  322. package/esm/themes/auditore-theme.js +1 -0
  323. package/esm/themes/auditore-theme.js.map +1 -1
  324. package/esm/themes/black-mesa-theme.d.ts +1 -0
  325. package/esm/themes/black-mesa-theme.d.ts.map +1 -1
  326. package/esm/themes/black-mesa-theme.js +1 -0
  327. package/esm/themes/black-mesa-theme.js.map +1 -1
  328. package/esm/themes/chieftain-theme.d.ts +1 -0
  329. package/esm/themes/chieftain-theme.d.ts.map +1 -1
  330. package/esm/themes/chieftain-theme.js +1 -0
  331. package/esm/themes/chieftain-theme.js.map +1 -1
  332. package/esm/themes/default-dark-theme.d.ts +1 -0
  333. package/esm/themes/default-dark-theme.d.ts.map +1 -1
  334. package/esm/themes/default-dark-theme.js +1 -0
  335. package/esm/themes/default-dark-theme.js.map +1 -1
  336. package/esm/themes/default-light-theme.d.ts +1 -0
  337. package/esm/themes/default-light-theme.d.ts.map +1 -1
  338. package/esm/themes/default-light-theme.js +1 -0
  339. package/esm/themes/default-light-theme.js.map +1 -1
  340. package/esm/themes/dragonborn-theme.d.ts +1 -0
  341. package/esm/themes/dragonborn-theme.d.ts.map +1 -1
  342. package/esm/themes/dragonborn-theme.js +1 -0
  343. package/esm/themes/dragonborn-theme.js.map +1 -1
  344. package/esm/themes/hawkins-theme.d.ts +1 -0
  345. package/esm/themes/hawkins-theme.d.ts.map +1 -1
  346. package/esm/themes/hawkins-theme.js +1 -0
  347. package/esm/themes/hawkins-theme.js.map +1 -1
  348. package/esm/themes/jedi-theme.d.ts +1 -0
  349. package/esm/themes/jedi-theme.d.ts.map +1 -1
  350. package/esm/themes/jedi-theme.js +1 -0
  351. package/esm/themes/jedi-theme.js.map +1 -1
  352. package/esm/themes/neon-runner-theme.d.ts +1 -0
  353. package/esm/themes/neon-runner-theme.d.ts.map +1 -1
  354. package/esm/themes/neon-runner-theme.js +1 -0
  355. package/esm/themes/neon-runner-theme.js.map +1 -1
  356. package/esm/themes/paladin-theme.d.ts +1 -0
  357. package/esm/themes/paladin-theme.d.ts.map +1 -1
  358. package/esm/themes/paladin-theme.js +1 -0
  359. package/esm/themes/paladin-theme.js.map +1 -1
  360. package/esm/themes/plumber-theme.d.ts +1 -0
  361. package/esm/themes/plumber-theme.d.ts.map +1 -1
  362. package/esm/themes/plumber-theme.js +1 -0
  363. package/esm/themes/plumber-theme.js.map +1 -1
  364. package/esm/themes/replicant-theme.d.ts +1 -0
  365. package/esm/themes/replicant-theme.d.ts.map +1 -1
  366. package/esm/themes/replicant-theme.js +1 -0
  367. package/esm/themes/replicant-theme.js.map +1 -1
  368. package/esm/themes/sandworm-theme.d.ts +1 -0
  369. package/esm/themes/sandworm-theme.d.ts.map +1 -1
  370. package/esm/themes/sandworm-theme.js +1 -0
  371. package/esm/themes/sandworm-theme.js.map +1 -1
  372. package/esm/themes/shadow-broker-theme.d.ts +1 -0
  373. package/esm/themes/shadow-broker-theme.d.ts.map +1 -1
  374. package/esm/themes/shadow-broker-theme.js +1 -0
  375. package/esm/themes/shadow-broker-theme.js.map +1 -1
  376. package/esm/themes/sith-theme.d.ts +1 -0
  377. package/esm/themes/sith-theme.d.ts.map +1 -1
  378. package/esm/themes/sith-theme.js +1 -0
  379. package/esm/themes/sith-theme.js.map +1 -1
  380. package/esm/themes/vault-dweller-theme.d.ts +1 -0
  381. package/esm/themes/vault-dweller-theme.d.ts.map +1 -1
  382. package/esm/themes/vault-dweller-theme.js +1 -0
  383. package/esm/themes/vault-dweller-theme.js.map +1 -1
  384. package/esm/themes/wild-hunt-theme.d.ts +1 -0
  385. package/esm/themes/wild-hunt-theme.d.ts.map +1 -1
  386. package/esm/themes/wild-hunt-theme.js +1 -0
  387. package/esm/themes/wild-hunt-theme.js.map +1 -1
  388. package/esm/themes/xenomorph-theme.d.ts +1 -0
  389. package/esm/themes/xenomorph-theme.d.ts.map +1 -1
  390. package/esm/themes/xenomorph-theme.js +1 -0
  391. package/esm/themes/xenomorph-theme.js.map +1 -1
  392. package/package.json +3 -3
  393. package/src/components/accordion/accordion-item.tsx +10 -15
  394. package/src/components/accordion/accordion.spec.tsx +134 -79
  395. package/src/components/accordion/accordion.tsx +14 -2
  396. package/src/components/alert.tsx +1 -1
  397. package/src/components/app-bar-link.tsx +1 -1
  398. package/src/components/app-bar.tsx +1 -1
  399. package/src/components/avatar.tsx +1 -1
  400. package/src/components/badge.tsx +1 -1
  401. package/src/components/breadcrumb.spec.tsx +3 -3
  402. package/src/components/breadcrumb.tsx +1 -1
  403. package/src/components/button-group.tsx +4 -4
  404. package/src/components/button.spec.tsx +1 -1
  405. package/src/components/button.tsx +1 -1
  406. package/src/components/cache-view.spec.tsx +2 -2
  407. package/src/components/cache-view.tsx +3 -3
  408. package/src/components/card.tsx +5 -5
  409. package/src/components/carousel.tsx +2 -2
  410. package/src/components/chip.spec.tsx +64 -0
  411. package/src/components/chip.tsx +5 -2
  412. package/src/components/circular-progress.spec.tsx +20 -14
  413. package/src/components/circular-progress.tsx +5 -11
  414. package/src/components/command-palette/command-palette-input.tsx +1 -1
  415. package/src/components/command-palette/command-palette-suggestion-list.spec.tsx +1 -1
  416. package/src/components/command-palette/command-palette-suggestion-list.tsx +1 -1
  417. package/src/components/command-palette/index.spec.tsx +95 -33
  418. package/src/components/command-palette/index.tsx +16 -4
  419. package/src/components/context-menu/context-menu-item.tsx +1 -1
  420. package/src/components/context-menu/context-menu.tsx +1 -1
  421. package/src/components/data-grid/body.tsx +1 -1
  422. package/src/components/data-grid/data-grid-row.tsx +21 -3
  423. package/src/components/data-grid/data-grid.spec.tsx +246 -92
  424. package/src/components/data-grid/data-grid.tsx +52 -21
  425. package/src/components/data-grid/filters/boolean-filter.spec.tsx +29 -18
  426. package/src/components/data-grid/filters/boolean-filter.tsx +6 -6
  427. package/src/components/data-grid/filters/date-filter.spec.tsx +35 -22
  428. package/src/components/data-grid/filters/date-filter.tsx +8 -8
  429. package/src/components/data-grid/filters/enum-filter.spec.tsx +35 -20
  430. package/src/components/data-grid/filters/enum-filter.tsx +7 -7
  431. package/src/components/data-grid/filters/filter-dropdown.tsx +1 -1
  432. package/src/components/data-grid/filters/number-filter.spec.tsx +32 -22
  433. package/src/components/data-grid/filters/number-filter.tsx +7 -7
  434. package/src/components/data-grid/filters/string-filter.spec.tsx +32 -20
  435. package/src/components/data-grid/filters/string-filter.tsx +7 -7
  436. package/src/components/data-grid/footer.spec.tsx +79 -31
  437. package/src/components/data-grid/footer.tsx +10 -15
  438. package/src/components/data-grid/header.spec.tsx +152 -68
  439. package/src/components/data-grid/header.tsx +64 -27
  440. package/src/components/data-grid/selection-cell.tsx +2 -1
  441. package/src/components/dialog.spec.tsx +77 -2
  442. package/src/components/dialog.tsx +15 -2
  443. package/src/components/divider.tsx +1 -1
  444. package/src/components/drawer/drawer-toggle-button.tsx +1 -1
  445. package/src/components/drawer/index.tsx +1 -1
  446. package/src/components/dropdown.spec.tsx +9 -0
  447. package/src/components/dropdown.tsx +2 -1
  448. package/src/components/fab.tsx +1 -1
  449. package/src/components/form.tsx +1 -1
  450. package/src/components/grid.tsx +1 -1
  451. package/src/components/icons/icon.tsx +1 -1
  452. package/src/components/image.spec.tsx +82 -0
  453. package/src/components/image.tsx +18 -9
  454. package/src/components/inputs/autocomplete.tsx +1 -1
  455. package/src/components/inputs/checkbox.spec.tsx +1 -1
  456. package/src/components/inputs/checkbox.tsx +2 -1
  457. package/src/components/inputs/input-number.spec.tsx +1 -1
  458. package/src/components/inputs/input-number.tsx +1 -1
  459. package/src/components/inputs/input.spec.tsx +1 -1
  460. package/src/components/inputs/input.tsx +1 -1
  461. package/src/components/inputs/radio-group.spec.tsx +1 -1
  462. package/src/components/inputs/radio-group.tsx +1 -1
  463. package/src/components/inputs/radio.spec.tsx +1 -1
  464. package/src/components/inputs/radio.tsx +2 -1
  465. package/src/components/inputs/select.tsx +1 -1
  466. package/src/components/inputs/slider.tsx +2 -1
  467. package/src/components/inputs/switch.spec.tsx +1 -1
  468. package/src/components/inputs/switch.tsx +2 -1
  469. package/src/components/inputs/text-area.spec.tsx +1 -1
  470. package/src/components/inputs/text-area.tsx +1 -1
  471. package/src/components/linear-progress.spec.tsx +22 -18
  472. package/src/components/linear-progress.tsx +5 -11
  473. package/src/components/list/list-item.tsx +23 -5
  474. package/src/components/list/list.spec.tsx +165 -32
  475. package/src/components/list/list.tsx +38 -11
  476. package/src/components/loader.spec.tsx +1 -1
  477. package/src/components/loader.tsx +1 -1
  478. package/src/components/markdown/markdown-display.spec.tsx +133 -1
  479. package/src/components/markdown/markdown-display.tsx +13 -2
  480. package/src/components/markdown/markdown-editor.spec.tsx +124 -1
  481. package/src/components/markdown/markdown-editor.tsx +1 -1
  482. package/src/components/markdown/markdown-input.spec.tsx +1 -1
  483. package/src/components/markdown/markdown-input.tsx +1 -1
  484. package/src/components/menu/menu.tsx +2 -2
  485. package/src/components/modal.spec.tsx +127 -4
  486. package/src/components/modal.tsx +42 -4
  487. package/src/components/noty-list.tsx +2 -2
  488. package/src/components/page-container/index.tsx +1 -1
  489. package/src/components/page-container/page-header.tsx +1 -1
  490. package/src/components/page-layout/index.spec.tsx +20 -0
  491. package/src/components/page-layout/index.tsx +2 -2
  492. package/src/components/pagination.tsx +1 -1
  493. package/src/components/paper.tsx +1 -1
  494. package/src/components/rating.spec.tsx +200 -5
  495. package/src/components/rating.tsx +29 -23
  496. package/src/components/result.tsx +1 -1
  497. package/src/components/skeleton.tsx +1 -1
  498. package/src/components/suggest/index.spec.tsx +148 -44
  499. package/src/components/suggest/index.tsx +16 -3
  500. package/src/components/suggest/suggest-input.spec.tsx +1 -1
  501. package/src/components/suggest/suggest-input.tsx +1 -1
  502. package/src/components/suggest/suggestion-list.spec.tsx +1 -1
  503. package/src/components/suggest/suggestion-list.tsx +1 -1
  504. package/src/components/tabs.tsx +6 -2
  505. package/src/components/timeline.tsx +2 -2
  506. package/src/components/tooltip.tsx +1 -1
  507. package/src/components/tree/tree-item.tsx +20 -5
  508. package/src/components/tree/tree.spec.tsx +101 -2
  509. package/src/components/tree/tree.tsx +22 -4
  510. package/src/components/typography.tsx +1 -1
  511. package/src/components/wizard/index.spec.tsx +3 -3
  512. package/src/components/wizard/index.tsx +1 -1
  513. package/src/services/collection-service.spec.ts +33 -24
  514. package/src/services/collection-service.ts +35 -13
  515. package/src/services/css-variable-theme.spec.ts +1 -0
  516. package/src/services/css-variable-theme.ts +25 -0
  517. package/src/services/list-service.spec.ts +13 -42
  518. package/src/services/list-service.ts +15 -13
  519. package/src/services/theme-provider-service.ts +2 -0
  520. package/src/services/tree-service.spec.ts +12 -9
  521. package/src/services/tree-service.ts +5 -8
  522. package/src/themes/architect-theme.ts +1 -0
  523. package/src/themes/auditore-theme.ts +1 -0
  524. package/src/themes/black-mesa-theme.ts +1 -0
  525. package/src/themes/chieftain-theme.ts +1 -0
  526. package/src/themes/default-dark-theme.ts +1 -0
  527. package/src/themes/default-light-theme.ts +1 -0
  528. package/src/themes/dragonborn-theme.ts +1 -0
  529. package/src/themes/hawkins-theme.ts +1 -0
  530. package/src/themes/jedi-theme.ts +1 -0
  531. package/src/themes/neon-runner-theme.ts +1 -0
  532. package/src/themes/paladin-theme.ts +1 -0
  533. package/src/themes/plumber-theme.ts +1 -0
  534. package/src/themes/replicant-theme.ts +1 -0
  535. package/src/themes/sandworm-theme.ts +1 -0
  536. package/src/themes/shadow-broker-theme.ts +1 -0
  537. package/src/themes/sith-theme.ts +1 -0
  538. package/src/themes/vault-dweller-theme.ts +1 -0
  539. package/src/themes/wild-hunt-theme.ts +1 -0
  540. package/src/themes/xenomorph-theme.ts +1 -0
@@ -14,7 +14,7 @@ describe('MarkdownDisplay', () => {
14
14
  vi.restoreAllMocks()
15
15
  })
16
16
 
17
- it('should render with shadow DOM', async () => {
17
+ it('should render as custom element', async () => {
18
18
  await usingAsync(new Injector(), async (injector) => {
19
19
  const rootElement = document.getElementById('root') as HTMLDivElement
20
20
 
@@ -240,4 +240,136 @@ describe('MarkdownDisplay', () => {
240
240
  expect(root?.children.length).toBe(0)
241
241
  })
242
242
  })
243
+
244
+ describe('keyboard navigation', () => {
245
+ it('should make links focusable', async () => {
246
+ await usingAsync(new Injector(), async (injector) => {
247
+ const rootElement = document.getElementById('root') as HTMLDivElement
248
+
249
+ initializeShadeRoot({
250
+ injector,
251
+ rootElement,
252
+ jsxElement: <MarkdownDisplay content="[Link A](https://a.com) and [Link B](https://b.com)" />,
253
+ })
254
+
255
+ await flushUpdates()
256
+
257
+ const links = document.querySelectorAll<HTMLAnchorElement>('shade-markdown-display .md-link')
258
+ expect(links.length).toBe(2)
259
+
260
+ links[0].focus()
261
+ expect(document.activeElement).toBe(links[0])
262
+
263
+ links[1].focus()
264
+ expect(document.activeElement).toBe(links[1])
265
+ })
266
+ })
267
+
268
+ it('should make code blocks focusable via tabIndex', async () => {
269
+ await usingAsync(new Injector(), async (injector) => {
270
+ const rootElement = document.getElementById('root') as HTMLDivElement
271
+
272
+ initializeShadeRoot({
273
+ injector,
274
+ rootElement,
275
+ jsxElement: <MarkdownDisplay content={'```js\nconsole.log("hi")\n```'} />,
276
+ })
277
+
278
+ await flushUpdates()
279
+
280
+ const codeBlock = document.querySelector('shade-markdown-display .md-code-block') as HTMLPreElement
281
+ expect(codeBlock).not.toBeNull()
282
+ expect(codeBlock.tabIndex).toBe(0)
283
+
284
+ codeBlock.focus()
285
+ expect(document.activeElement).toBe(codeBlock)
286
+ })
287
+ })
288
+
289
+ it('should make checkbox inputs focusable when not disabled', async () => {
290
+ await usingAsync(new Injector(), async (injector) => {
291
+ const rootElement = document.getElementById('root') as HTMLDivElement
292
+
293
+ initializeShadeRoot({
294
+ injector,
295
+ rootElement,
296
+ jsxElement: <MarkdownDisplay content={'- [ ] Task A\n- [ ] Task B'} readOnly={false} onChange={() => {}} />,
297
+ })
298
+
299
+ await flushUpdates()
300
+
301
+ const checkboxes = document.querySelectorAll<HTMLInputElement>(
302
+ 'shade-markdown-display shade-checkbox input[type="checkbox"]',
303
+ )
304
+ expect(checkboxes.length).toBe(2)
305
+
306
+ checkboxes[0].focus()
307
+ expect(document.activeElement).toBe(checkboxes[0])
308
+ expect(checkboxes[0].disabled).toBe(false)
309
+ })
310
+ })
311
+
312
+ it('should toggle checkbox via keyboard activation', async () => {
313
+ await usingAsync(new Injector(), async (injector) => {
314
+ const rootElement = document.getElementById('root') as HTMLDivElement
315
+ const onChange = vi.fn()
316
+
317
+ initializeShadeRoot({
318
+ injector,
319
+ rootElement,
320
+ jsxElement: <MarkdownDisplay content="- [ ] My Task" readOnly={false} onChange={onChange} />,
321
+ })
322
+
323
+ await flushUpdates()
324
+
325
+ const input = document.querySelector(
326
+ 'shade-markdown-display shade-checkbox input[type="checkbox"]',
327
+ ) as HTMLInputElement
328
+ expect(input).not.toBeNull()
329
+
330
+ input.focus()
331
+ expect(document.activeElement).toBe(input)
332
+
333
+ input.click()
334
+ await flushUpdates()
335
+
336
+ expect(onChange).toHaveBeenCalledOnce()
337
+ expect(onChange).toHaveBeenCalledWith('- [x] My Task')
338
+ })
339
+ })
340
+
341
+ it('should have correct focus order for mixed interactive elements', async () => {
342
+ await usingAsync(new Injector(), async (injector) => {
343
+ const rootElement = document.getElementById('root') as HTMLDivElement
344
+
345
+ const content = [
346
+ '[First link](https://first.com)',
347
+ '',
348
+ '```js',
349
+ 'code()',
350
+ '```',
351
+ '',
352
+ '[Second link](https://second.com)',
353
+ ].join('\n')
354
+
355
+ initializeShadeRoot({
356
+ injector,
357
+ rootElement,
358
+ jsxElement: <MarkdownDisplay content={content} />,
359
+ })
360
+
361
+ await flushUpdates()
362
+
363
+ const focusableElements = document.querySelectorAll(
364
+ 'shade-markdown-display a[href], shade-markdown-display [tabindex="0"]',
365
+ )
366
+ expect(focusableElements.length).toBe(3)
367
+
368
+ const [firstLink, codeBlock, secondLink] = focusableElements
369
+ expect(firstLink.tagName).toBe('A')
370
+ expect(codeBlock.tagName).toBe('PRE')
371
+ expect(secondLink.tagName).toBe('A')
372
+ })
373
+ })
374
+ })
243
375
  })
@@ -60,7 +60,7 @@ const renderBlock = (
60
60
  return <Typography variant="body1">{renderInline(node.children)}</Typography>
61
61
  case 'codeBlock':
62
62
  return (
63
- <pre className="md-code-block" data-language={node.language || undefined}>
63
+ <pre className="md-code-block" data-language={node.language || undefined} tabIndex={0}>
64
64
  <code>{node.content}</code>
65
65
  </pre>
66
66
  )
@@ -105,7 +105,7 @@ const renderBlock = (
105
105
  * blockquotes, images, links, and horizontal rules.
106
106
  */
107
107
  export const MarkdownDisplay = Shade<MarkdownDisplayProps>({
108
- shadowDomName: 'shade-markdown-display',
108
+ customElementName: 'shade-markdown-display',
109
109
  css: {
110
110
  display: 'block',
111
111
  fontFamily: cssVariableTheme.typography.fontFamily,
@@ -136,6 +136,11 @@ export const MarkdownDisplay = Shade<MarkdownDisplayProps>({
136
136
  whiteSpace: 'pre',
137
137
  },
138
138
 
139
+ '& .md-code-block:focus-visible': {
140
+ outline: cssVariableTheme.action.focusOutline,
141
+ outlineOffset: '-2px',
142
+ },
143
+
139
144
  '& .md-blockquote': {
140
145
  borderLeft: `4px solid ${cssVariableTheme.palette.primary.main}`,
141
146
  margin: `${cssVariableTheme.spacing.sm} 0`,
@@ -150,6 +155,12 @@ export const MarkdownDisplay = Shade<MarkdownDisplayProps>({
150
155
  '& .md-link:hover': {
151
156
  textDecoration: 'underline',
152
157
  },
158
+ '& .md-link:focus-visible': {
159
+ textDecoration: 'underline',
160
+ outline: cssVariableTheme.action.focusOutline,
161
+ outlineOffset: '2px',
162
+ borderRadius: cssVariableTheme.shape.borderRadius.xs,
163
+ },
153
164
 
154
165
  '& .md-image': {
155
166
  maxWidth: '100%',
@@ -14,7 +14,7 @@ describe('MarkdownEditor', () => {
14
14
  vi.restoreAllMocks()
15
15
  })
16
16
 
17
- it('should render with shadow DOM', async () => {
17
+ it('should render as custom element', async () => {
18
18
  await usingAsync(new Injector(), async (injector) => {
19
19
  const rootElement = document.getElementById('root') as HTMLDivElement
20
20
 
@@ -400,4 +400,127 @@ describe('MarkdownEditor', () => {
400
400
  })
401
401
  })
402
402
  })
403
+
404
+ describe('keyboard navigation', () => {
405
+ it('should have a focusable textarea in side-by-side layout', async () => {
406
+ await usingAsync(new Injector(), async (injector) => {
407
+ const rootElement = document.getElementById('root') as HTMLDivElement
408
+
409
+ initializeShadeRoot({
410
+ injector,
411
+ rootElement,
412
+ jsxElement: <MarkdownEditor value="# Hello" />,
413
+ })
414
+
415
+ await flushUpdates()
416
+
417
+ const textarea = document.querySelector('shade-markdown-editor textarea') as HTMLTextAreaElement
418
+ expect(textarea).not.toBeNull()
419
+
420
+ textarea.focus()
421
+ expect(document.activeElement).toBe(textarea)
422
+ })
423
+ })
424
+
425
+ it('should have focusable tab buttons in tabs layout', async () => {
426
+ await usingAsync(new Injector(), async (injector) => {
427
+ const rootElement = document.getElementById('root') as HTMLDivElement
428
+
429
+ initializeShadeRoot({
430
+ injector,
431
+ rootElement,
432
+ jsxElement: <MarkdownEditor value="# Hello" layout="tabs" />,
433
+ })
434
+
435
+ await flushUpdates()
436
+
437
+ const tabButtons = document.querySelectorAll<HTMLButtonElement>('shade-markdown-editor .shade-tab-btn')
438
+ expect(tabButtons.length).toBe(2)
439
+
440
+ tabButtons[0].focus()
441
+ expect(document.activeElement).toBe(tabButtons[0])
442
+
443
+ tabButtons[1].focus()
444
+ expect(document.activeElement).toBe(tabButtons[1])
445
+ })
446
+ })
447
+
448
+ it('should use tabIndex to indicate active tab in controlled tabs layout', async () => {
449
+ await usingAsync(new Injector(), async (injector) => {
450
+ const rootElement = document.getElementById('root') as HTMLDivElement
451
+
452
+ initializeShadeRoot({
453
+ injector,
454
+ rootElement,
455
+ jsxElement: <MarkdownEditor value="# Hello" layout="tabs" />,
456
+ })
457
+
458
+ await flushUpdates()
459
+
460
+ const tabButtons = document.querySelectorAll<HTMLButtonElement>('shade-markdown-editor .shade-tab-btn')
461
+
462
+ const activeTab = Array.from(tabButtons).find((btn) => btn.classList.contains('active'))
463
+ const inactiveTab = Array.from(tabButtons).find((btn) => !btn.classList.contains('active'))
464
+
465
+ expect(activeTab).not.toBeUndefined()
466
+ expect(inactiveTab).not.toBeUndefined()
467
+ expect(activeTab?.tabIndex).toBe(0)
468
+ expect(inactiveTab?.tabIndex).toBe(-1)
469
+ })
470
+ })
471
+
472
+ it('should switch tabs when tab button is clicked via keyboard activation', async () => {
473
+ await usingAsync(new Injector(), async (injector) => {
474
+ const rootElement = document.getElementById('root') as HTMLDivElement
475
+
476
+ initializeShadeRoot({
477
+ injector,
478
+ rootElement,
479
+ jsxElement: <MarkdownEditor value="# Hello" layout="tabs" />,
480
+ })
481
+
482
+ await flushUpdates()
483
+
484
+ const tabButtons = document.querySelectorAll<HTMLButtonElement>('shade-markdown-editor .shade-tab-btn')
485
+
486
+ const previewButton = Array.from(tabButtons).find((btn) => btn.textContent?.includes('Preview'))
487
+ expect(previewButton).not.toBeUndefined()
488
+
489
+ previewButton!.click()
490
+ await flushUpdates()
491
+
492
+ const display = document.querySelector('shade-markdown-editor shade-markdown-display')
493
+ expect(display).not.toBeNull()
494
+ })
495
+ })
496
+
497
+ it('should have focusable interactive elements in preview pane', async () => {
498
+ await usingAsync(new Injector(), async (injector) => {
499
+ const rootElement = document.getElementById('root') as HTMLDivElement
500
+
501
+ initializeShadeRoot({
502
+ injector,
503
+ rootElement,
504
+ jsxElement: (
505
+ <MarkdownEditor
506
+ value={'- [ ] Task A\n- [x] Task B\n\n[A link](https://example.com)'}
507
+ onValueChange={() => {}}
508
+ />
509
+ ),
510
+ })
511
+
512
+ await flushUpdates()
513
+
514
+ const checkboxes = document.querySelectorAll(
515
+ 'shade-markdown-editor shade-markdown-display shade-checkbox input[type="checkbox"]',
516
+ )
517
+ expect(checkboxes.length).toBe(2)
518
+
519
+ const link = document.querySelector('shade-markdown-editor shade-markdown-display .md-link')
520
+ expect(link).not.toBeNull()
521
+ ;(checkboxes[0] as HTMLElement).focus()
522
+ expect(document.activeElement).toBe(checkboxes[0])
523
+ })
524
+ })
525
+ })
403
526
  })
@@ -32,7 +32,7 @@ type TabType = 'edit' | 'preview'
32
32
  * Supports three layouts: side-by-side, tabs (Edit/Preview), or above-below.
33
33
  */
34
34
  export const MarkdownEditor = Shade<MarkdownEditorProps>({
35
- shadowDomName: 'shade-markdown-editor',
35
+ customElementName: 'shade-markdown-editor',
36
36
  css: {
37
37
  display: 'flex',
38
38
  fontFamily: cssVariableTheme.typography.fontFamily,
@@ -15,7 +15,7 @@ describe('MarkdownInput', () => {
15
15
  vi.restoreAllMocks()
16
16
  })
17
17
 
18
- it('should render with shadow DOM', async () => {
18
+ it('should render as custom element', async () => {
19
19
  await usingAsync(new Injector(), async (injector) => {
20
20
  const rootElement = document.getElementById('root') as HTMLDivElement
21
21
 
@@ -41,7 +41,7 @@ export type MarkdownInputProps = {
41
41
  * it is inlined as a `![pasted image](data:...)` Markdown image.
42
42
  */
43
43
  export const MarkdownInput = Shade<MarkdownInputProps>({
44
- shadowDomName: 'shade-markdown-input',
44
+ customElementName: 'shade-markdown-input',
45
45
  css: {
46
46
  display: 'block',
47
47
  fontFamily: cssVariableTheme.typography.fontFamily,
@@ -97,7 +97,7 @@ const renderItems = (
97
97
  }
98
98
 
99
99
  export const Menu = Shade<MenuProps>({
100
- shadowDomName: 'shade-menu',
100
+ customElementName: 'shade-menu',
101
101
  css: {
102
102
  display: 'flex',
103
103
  outline: 'none',
@@ -250,7 +250,7 @@ export const Menu = Shade<MenuProps>({
250
250
  useHostProps({
251
251
  role: mode === 'horizontal' ? 'menubar' : 'menu',
252
252
  'data-mode': mode,
253
- tabindex: '0',
253
+ tabIndex: 0,
254
254
  })
255
255
 
256
256
  useHostProps({
@@ -1,5 +1,5 @@
1
1
  import { Injector } from '@furystack/inject'
2
- import { initializeShadeRoot, createComponent, Shade, flushUpdates } from '@furystack/shades'
2
+ import { initializeShadeRoot, createComponent, Shade, flushUpdates, SpatialNavigationService } from '@furystack/shades'
3
3
  import { usingAsync } from '@furystack/utils'
4
4
  import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
5
5
  import { Modal } from './modal.js'
@@ -58,7 +58,7 @@ describe('Modal', () => {
58
58
  let setVisible!: (v: boolean) => void
59
59
 
60
60
  const Wrapper = Shade({
61
- shadowDomName: 'modal-visibility-test-show',
61
+ customElementName: 'modal-visibility-test-show',
62
62
  render: ({ useState }) => {
63
63
  const [visible, setter] = useState('visible', false)
64
64
  setVisible = setter
@@ -89,7 +89,7 @@ describe('Modal', () => {
89
89
  let setVisible!: (v: boolean) => void
90
90
 
91
91
  const Wrapper = Shade({
92
- shadowDomName: 'modal-visibility-test-hide',
92
+ customElementName: 'modal-visibility-test-hide',
93
93
  render: ({ useState }) => {
94
94
  const [visible, setter] = useState('visible', true)
95
95
  setVisible = setter
@@ -203,7 +203,7 @@ describe('Modal', () => {
203
203
  const showAnimation = vi.fn()
204
204
 
205
205
  const Wrapper = Shade({
206
- shadowDomName: 'modal-show-animation-test',
206
+ customElementName: 'modal-show-animation-test',
207
207
  render: ({ useState }) => {
208
208
  const [visible, setter] = useState('visible', false)
209
209
  setVisible = setter
@@ -308,4 +308,127 @@ describe('Modal', () => {
308
308
  })
309
309
  })
310
310
  })
311
+
312
+ describe('spatial navigation', () => {
313
+ it('should render with data-nav-section attribute when visible', async () => {
314
+ await usingAsync(new Injector(), async (injector) => {
315
+ const rootElement = document.getElementById('root') as HTMLDivElement
316
+
317
+ initializeShadeRoot({
318
+ injector,
319
+ rootElement,
320
+ jsxElement: (
321
+ <Modal isVisible={true}>
322
+ <div>Content</div>
323
+ </Modal>
324
+ ),
325
+ })
326
+
327
+ await flushUpdates()
328
+ const backdrop = document.querySelector('.shade-backdrop')
329
+ expect(backdrop?.getAttribute('data-nav-section')).toBeTruthy()
330
+ })
331
+ })
332
+
333
+ it('should render with custom navSection name', async () => {
334
+ await usingAsync(new Injector(), async (injector) => {
335
+ const rootElement = document.getElementById('root') as HTMLDivElement
336
+
337
+ initializeShadeRoot({
338
+ injector,
339
+ rootElement,
340
+ jsxElement: (
341
+ <Modal isVisible={true} navSection="my-modal">
342
+ <div>Content</div>
343
+ </Modal>
344
+ ),
345
+ })
346
+
347
+ await flushUpdates()
348
+ const backdrop = document.querySelector('.shade-backdrop')
349
+ expect(backdrop?.getAttribute('data-nav-section')).toBe('my-modal')
350
+ })
351
+ })
352
+
353
+ it('should push focus trap when trapFocus is true and service is active', async () => {
354
+ await usingAsync(new Injector(), async (injector) => {
355
+ const spatialNav = injector.getInstance(SpatialNavigationService)
356
+ const pushSpy = vi.spyOn(spatialNav, 'pushFocusTrap')
357
+
358
+ const rootElement = document.getElementById('root') as HTMLDivElement
359
+
360
+ initializeShadeRoot({
361
+ injector,
362
+ rootElement,
363
+ jsxElement: (
364
+ <Modal isVisible={true} trapFocus={true} navSection="trapped-modal">
365
+ <div>Content</div>
366
+ </Modal>
367
+ ),
368
+ })
369
+
370
+ await flushUpdates()
371
+
372
+ expect(pushSpy).toHaveBeenCalledWith('trapped-modal')
373
+ expect(spatialNav.activeSection.getValue()).toBe('trapped-modal')
374
+ })
375
+ })
376
+
377
+ it('should not push focus trap when trapFocus is false', async () => {
378
+ await usingAsync(new Injector(), async (injector) => {
379
+ const spatialNav = injector.getInstance(SpatialNavigationService)
380
+ const pushSpy = vi.spyOn(spatialNav, 'pushFocusTrap')
381
+
382
+ const rootElement = document.getElementById('root') as HTMLDivElement
383
+
384
+ initializeShadeRoot({
385
+ injector,
386
+ rootElement,
387
+ jsxElement: (
388
+ <Modal isVisible={true} trapFocus={false}>
389
+ <div>Content</div>
390
+ </Modal>
391
+ ),
392
+ })
393
+
394
+ await flushUpdates()
395
+
396
+ expect(pushSpy).not.toHaveBeenCalled()
397
+ spatialNav.activeSection.setValue('other-section')
398
+ expect(spatialNav.activeSection.getValue()).toBe('other-section')
399
+ })
400
+ })
401
+
402
+ it('should pop focus trap when visibility changes from true to false', async () => {
403
+ let setVisible!: (v: boolean) => void
404
+
405
+ const Wrapper = Shade({
406
+ customElementName: 'modal-trap-visibility-test',
407
+ render: ({ useState }) => {
408
+ const [visible, setter] = useState('visible', true)
409
+ setVisible = setter
410
+ return (
411
+ <Modal isVisible={visible} trapFocus={true} navSection="trap-test">
412
+ <div>Content</div>
413
+ </Modal>
414
+ )
415
+ },
416
+ })
417
+
418
+ await usingAsync(new Injector(), async (injector) => {
419
+ const spatialNav = injector.getInstance(SpatialNavigationService)
420
+ const rootElement = document.getElementById('root') as HTMLDivElement
421
+
422
+ initializeShadeRoot({ injector, rootElement, jsxElement: <Wrapper /> })
423
+ await flushUpdates()
424
+
425
+ expect(spatialNav.activeSection.getValue()).toBe('trap-test')
426
+
427
+ setVisible(false)
428
+ await flushUpdates()
429
+
430
+ expect(spatialNav.activeSection.getValue()).not.toBe('trap-test')
431
+ })
432
+ })
433
+ })
311
434
  })
@@ -1,16 +1,28 @@
1
- import { Shade, createComponent } from '@furystack/shades'
1
+ import { Shade, createComponent, SpatialNavigationService } from '@furystack/shades'
2
2
  import { cssVariableTheme } from '../services/css-variable-theme.js'
3
3
 
4
+ let nextModalId = 0
5
+
4
6
  export type ModalProps = {
5
7
  backdropStyle?: Partial<CSSStyleDeclaration>
6
8
  isVisible: boolean
7
9
  onClose?: () => void
8
10
  showAnimation?: (el: Element | null) => Promise<unknown>
9
11
  hideAnimation?: (el: Element | null) => Promise<unknown>
12
+ /**
13
+ * When true, traps spatial navigation within the modal's bounds.
14
+ * If SpatialNavigationService is not yet instantiated, it will be created with defaults.
15
+ */
16
+ trapFocus?: boolean
17
+ /**
18
+ * Section name for spatial navigation scoping.
19
+ * @default 'modal'
20
+ */
21
+ navSection?: string
10
22
  }
11
23
 
12
24
  export const Modal = Shade<ModalProps>({
13
- shadowDomName: 'shade-modal',
25
+ customElementName: 'shade-modal',
14
26
  css: {
15
27
  fontFamily: cssVariableTheme.typography.fontFamily,
16
28
  '& .shade-backdrop': {
@@ -22,9 +34,34 @@ export const Modal = Shade<ModalProps>({
22
34
  left: '0',
23
35
  },
24
36
  },
25
- render: ({ props, children, useRef }) => {
26
- const { isVisible } = props
37
+ render: ({ props, children, injector, useRef, useDisposable, useState }) => {
38
+ const { isVisible, trapFocus, navSection } = props
27
39
  const backdropRef = useRef<HTMLDivElement>('backdrop')
40
+ const [generatedSectionId] = useState('generatedSectionId', String(nextModalId++))
41
+ const sectionName = navSection ?? `modal-${generatedSectionId}`
42
+
43
+ useDisposable(
44
+ 'spatial-nav-trap',
45
+ () => {
46
+ if (!isVisible || !trapFocus) return { [Symbol.dispose]: () => {} }
47
+
48
+ const spatialNav = injector.getInstance(SpatialNavigationService)
49
+
50
+ const previousSection = spatialNav.activeSection.getValue()
51
+ spatialNav.pushFocusTrap(sectionName)
52
+
53
+ return {
54
+ [Symbol.dispose]: () => {
55
+ try {
56
+ spatialNav.popFocusTrap(sectionName, previousSection)
57
+ } catch {
58
+ // Service may already be disposed during injector teardown
59
+ }
60
+ },
61
+ }
62
+ },
63
+ [isVisible, trapFocus],
64
+ )
28
65
 
29
66
  if (isVisible) {
30
67
  queueMicrotask(() => {
@@ -36,6 +73,7 @@ export const Modal = Shade<ModalProps>({
36
73
  <div
37
74
  ref={backdropRef}
38
75
  className="shade-backdrop"
76
+ data-nav-section={sectionName}
39
77
  onclick={async () => {
40
78
  await props.hideAnimation?.(backdropRef.current)
41
79
  props.onClose?.()
@@ -23,7 +23,7 @@ export const getDefaultNotyTimeouts = (type: NotyModel['type']) => {
23
23
  }
24
24
 
25
25
  export const NotyComponent = Shade<{ model: NotyModel; onDismiss: () => void }>({
26
- shadowDomName: 'shade-noty',
26
+ customElementName: 'shade-noty',
27
27
  css: {
28
28
  margin: cssVariableTheme.spacing.xs,
29
29
  overflow: 'hidden',
@@ -141,7 +141,7 @@ export const NotyComponent = Shade<{ model: NotyModel; onDismiss: () => void }>(
141
141
  })
142
142
 
143
143
  export const NotyList = Shade({
144
- shadowDomName: 'shade-noty-list',
144
+ customElementName: 'shade-noty-list',
145
145
  css: {
146
146
  position: 'fixed',
147
147
  fontFamily: cssVariableTheme.typography.fontFamily,
@@ -50,7 +50,7 @@ export type PageContainerProps = {
50
50
  * ```
51
51
  */
52
52
  export const PageContainer = Shade<PageContainerProps>({
53
- shadowDomName: 'shade-page-container',
53
+ customElementName: 'shade-page-container',
54
54
  elementBase: HTMLDivElement,
55
55
  elementBaseName: 'div',
56
56
  css: {
@@ -56,7 +56,7 @@ export type PageHeaderProps = {
56
56
  * ```
57
57
  */
58
58
  export const PageHeader = Shade<PageHeaderProps>({
59
- shadowDomName: 'shade-page-header',
59
+ customElementName: 'shade-page-header',
60
60
  css: {
61
61
  display: 'block',
62
62
  fontFamily: cssVariableTheme.typography.fontFamily,
@@ -647,6 +647,26 @@ describe('PageLayout component', () => {
647
647
  })
648
648
  })
649
649
 
650
+ it('should set data-nav-section="content" on the content area for spatial navigation scoping', async () => {
651
+ await usingAsync(new Injector(), async (injector) => {
652
+ const rootElement = document.getElementById('root') as HTMLDivElement
653
+
654
+ initializeShadeRoot({
655
+ injector,
656
+ rootElement,
657
+ jsxElement: (
658
+ <PageLayout>
659
+ <div>Content</div>
660
+ </PageLayout>
661
+ ),
662
+ })
663
+
664
+ await flushUpdates()
665
+ const contentArea = document.querySelector('.page-layout-content')
666
+ expect(contentArea?.getAttribute('data-nav-section')).toBe('content')
667
+ })
668
+ })
669
+
650
670
  it('should set CSS variable for zero paddingTop when no AppBar is configured', async () => {
651
671
  await usingAsync(new Injector(), async (injector) => {
652
672
  const rootElement = document.getElementById('root') as HTMLDivElement