@gjsify/adwaita-web 0.53.0 → 0.55.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 (440) hide show
  1. package/dist/adwaita-web.css +8070 -3037
  2. package/dist/adwaita-web.css.map +1 -1
  3. package/lib/types/adw-application-window.spec.d.ts +1 -0
  4. package/lib/types/adw-bin.spec.d.ts +1 -0
  5. package/lib/types/adw-breakpoint-bin.spec.d.ts +1 -0
  6. package/lib/types/adw-clamp-scrollable.spec.d.ts +1 -0
  7. package/lib/types/adw-multi-layout-view.spec.d.ts +1 -0
  8. package/lib/types/adw-preferences-row.spec.d.ts +1 -0
  9. package/lib/types/adw-shortcuts.spec.d.ts +1 -0
  10. package/lib/types/adw-tab-bar.spec.d.ts +1 -0
  11. package/lib/types/adw-tab-button.spec.d.ts +1 -0
  12. package/lib/types/adw-tab-overview.spec.d.ts +1 -0
  13. package/lib/types/adw-view-switcher-sidebar.spec.d.ts +1 -0
  14. package/lib/types/adw-window.spec.d.ts +1 -0
  15. package/lib/types/breakpoints.d.ts +14 -3
  16. package/lib/types/construct-vectors.spec.d.ts +1 -0
  17. package/lib/types/elements/adw-application-window.d.ts +27 -0
  18. package/lib/types/elements/adw-bin.d.ts +17 -0
  19. package/lib/types/elements/adw-breakpoint-bin.d.ts +67 -0
  20. package/lib/types/elements/adw-clamp-scrollable.d.ts +38 -0
  21. package/lib/types/elements/adw-dialog.d.ts +10 -0
  22. package/lib/types/elements/adw-layout-slot.d.ts +19 -0
  23. package/lib/types/elements/adw-multi-layout-view.d.ts +101 -0
  24. package/lib/types/elements/adw-preferences-row.d.ts +31 -0
  25. package/lib/types/elements/adw-shortcuts-dialog.d.ts +13 -0
  26. package/lib/types/elements/adw-shortcuts-item.d.ts +36 -0
  27. package/lib/types/elements/adw-shortcuts-section.d.ts +14 -0
  28. package/lib/types/elements/adw-sidebar.d.ts +5 -0
  29. package/lib/types/elements/adw-switch-row.d.ts +3 -0
  30. package/lib/types/elements/adw-tab-bar.d.ts +108 -0
  31. package/lib/types/elements/adw-tab-button.d.ts +32 -0
  32. package/lib/types/elements/adw-tab-overview.d.ts +199 -0
  33. package/lib/types/elements/adw-tab-view.d.ts +19 -23
  34. package/lib/types/elements/adw-view-switcher-sidebar.d.ts +84 -0
  35. package/lib/types/elements/adw-window.d.ts +40 -0
  36. package/lib/types/elements/gtk-about-dialog.d.ts +146 -0
  37. package/lib/types/elements/gtk-application-window.d.ts +34 -0
  38. package/lib/types/elements/gtk-aspect-frame.d.ts +27 -0
  39. package/lib/types/elements/gtk-button.d.ts +9 -0
  40. package/lib/types/elements/gtk-calendar.d.ts +159 -0
  41. package/lib/types/elements/gtk-center-box.d.ts +39 -0
  42. package/lib/types/elements/gtk-color-dialog-button.d.ts +69 -0
  43. package/lib/types/elements/gtk-column-view.d.ts +50 -0
  44. package/lib/types/elements/gtk-drag-icon.d.ts +78 -0
  45. package/lib/types/elements/gtk-drawing-area.d.ts +95 -0
  46. package/lib/types/elements/gtk-editable-label.d.ts +33 -0
  47. package/lib/types/elements/gtk-emoji-chooser.d.ts +84 -0
  48. package/lib/types/elements/gtk-entry.d.ts +20 -1
  49. package/lib/types/elements/gtk-expander.d.ts +49 -0
  50. package/lib/types/elements/gtk-fixed.d.ts +13 -0
  51. package/lib/types/elements/gtk-flow-box-child.d.ts +14 -0
  52. package/lib/types/elements/gtk-flow-box.d.ts +87 -0
  53. package/lib/types/elements/gtk-font-dialog-button.d.ts +83 -0
  54. package/lib/types/elements/gtk-frame.d.ts +24 -0
  55. package/lib/types/elements/gtk-gl-area.d.ts +136 -0
  56. package/lib/types/elements/gtk-graphics-offload.d.ts +24 -0
  57. package/lib/types/elements/gtk-grid-view.d.ts +39 -0
  58. package/lib/types/elements/gtk-grid.d.ts +27 -0
  59. package/lib/types/elements/gtk-header-bar.d.ts +60 -0
  60. package/lib/types/elements/gtk-inscription.d.ts +64 -0
  61. package/lib/types/elements/gtk-label.d.ts +3 -0
  62. package/lib/types/elements/gtk-level-bar.d.ts +59 -0
  63. package/lib/types/elements/gtk-link-button.d.ts +29 -0
  64. package/lib/types/elements/gtk-list-box-row.d.ts +54 -0
  65. package/lib/types/elements/gtk-list-box.d.ts +100 -0
  66. package/lib/types/elements/gtk-list-view.d.ts +73 -0
  67. package/lib/types/elements/gtk-media-controls.d.ts +142 -0
  68. package/lib/types/elements/gtk-notebook.d.ts +148 -0
  69. package/lib/types/elements/gtk-overlay.d.ts +22 -0
  70. package/lib/types/elements/gtk-page-setup-unix-dialog.d.ts +87 -0
  71. package/lib/types/elements/gtk-paned.d.ts +121 -0
  72. package/lib/types/elements/gtk-password-entry.d.ts +27 -0
  73. package/lib/types/elements/gtk-picture.d.ts +66 -0
  74. package/lib/types/elements/gtk-popover-bin.d.ts +82 -0
  75. package/lib/types/elements/gtk-popover-menu-bar.d.ts +66 -0
  76. package/lib/types/elements/gtk-popover-menu.d.ts +83 -0
  77. package/lib/types/elements/gtk-popover.d.ts +28 -3
  78. package/lib/types/elements/gtk-print-unix-dialog.d.ts +167 -0
  79. package/lib/types/elements/gtk-revealer.d.ts +55 -0
  80. package/lib/types/elements/gtk-scale-button.d.ts +59 -0
  81. package/lib/types/elements/gtk-scale.d.ts +98 -0
  82. package/lib/types/elements/gtk-scrollbar.d.ts +124 -0
  83. package/lib/types/elements/gtk-scrolled-window.d.ts +121 -0
  84. package/lib/types/elements/gtk-search-bar.d.ts +39 -0
  85. package/lib/types/elements/gtk-search-entry.d.ts +27 -0
  86. package/lib/types/elements/gtk-separator.d.ts +10 -0
  87. package/lib/types/elements/gtk-spin-button.d.ts +94 -0
  88. package/lib/types/elements/gtk-spinner.d.ts +14 -0
  89. package/lib/types/elements/gtk-stack-sidebar.d.ts +43 -0
  90. package/lib/types/elements/gtk-stack-switcher.d.ts +58 -0
  91. package/lib/types/elements/gtk-stack.d.ts +154 -0
  92. package/lib/types/elements/gtk-switch.d.ts +4 -1
  93. package/lib/types/elements/gtk-text-view.d.ts +62 -0
  94. package/lib/types/elements/gtk-text.d.ts +45 -0
  95. package/lib/types/elements/gtk-toggle-button.d.ts +45 -0
  96. package/lib/types/elements/gtk-tree-expander.d.ts +38 -0
  97. package/lib/types/elements/gtk-video.d.ts +97 -0
  98. package/lib/types/elements/gtk-viewport.d.ts +84 -0
  99. package/lib/types/elements/gtk-window-controls.d.ts +80 -0
  100. package/lib/types/elements/gtk-window-handle.d.ts +39 -0
  101. package/lib/types/elements/gtk-window.d.ts +97 -0
  102. package/lib/types/elements/justification.d.ts +2 -0
  103. package/lib/types/elements/popover-menu.d.ts +18 -0
  104. package/lib/types/elements/roving-focus.d.ts +29 -1
  105. package/lib/types/emoji-data.d.ts +38 -0
  106. package/lib/types/font-description.d.ts +96 -0
  107. package/lib/types/gtk-about-dialog.spec.d.ts +1 -0
  108. package/lib/types/gtk-action-bar.spec.d.ts +1 -0
  109. package/lib/types/gtk-application-window.spec.d.ts +1 -0
  110. package/lib/types/gtk-aspect-frame.spec.d.ts +1 -0
  111. package/lib/types/gtk-calendar.spec.d.ts +1 -0
  112. package/lib/types/gtk-center-box.spec.d.ts +1 -0
  113. package/lib/types/gtk-color-dialog-button.spec.d.ts +1 -0
  114. package/lib/types/gtk-column-view.spec.d.ts +1 -0
  115. package/lib/types/gtk-drag-icon.spec.d.ts +1 -0
  116. package/lib/types/gtk-drawing-area.spec.d.ts +1 -0
  117. package/lib/types/gtk-editable-label.spec.d.ts +1 -0
  118. package/lib/types/gtk-emoji-chooser.spec.d.ts +1 -0
  119. package/lib/types/gtk-expander.spec.d.ts +1 -0
  120. package/lib/types/gtk-fixed.spec.d.ts +1 -0
  121. package/lib/types/gtk-flow-box.spec.d.ts +1 -0
  122. package/lib/types/gtk-font-dialog-button.spec.d.ts +1 -0
  123. package/lib/types/gtk-frame.spec.d.ts +1 -0
  124. package/lib/types/gtk-gl-area.spec.d.ts +1 -0
  125. package/lib/types/gtk-graphics-offload.spec.d.ts +1 -0
  126. package/lib/types/gtk-grid-view.spec.d.ts +1 -0
  127. package/lib/types/gtk-grid.spec.d.ts +1 -0
  128. package/lib/types/gtk-header-bar.spec.d.ts +1 -0
  129. package/lib/types/gtk-inscription.spec.d.ts +1 -0
  130. package/lib/types/gtk-level-bar.spec.d.ts +1 -0
  131. package/lib/types/gtk-link-button.spec.d.ts +1 -0
  132. package/lib/types/gtk-list-box.spec.d.ts +1 -0
  133. package/lib/types/gtk-list-view.spec.d.ts +1 -0
  134. package/lib/types/gtk-media-controls.spec.d.ts +1 -0
  135. package/lib/types/gtk-notebook.spec.d.ts +1 -0
  136. package/lib/types/gtk-overlay.spec.d.ts +1 -0
  137. package/lib/types/gtk-paned.spec.d.ts +1 -0
  138. package/lib/types/gtk-password-entry.spec.d.ts +1 -0
  139. package/lib/types/gtk-picture.spec.d.ts +1 -0
  140. package/lib/types/gtk-popover-bin.spec.d.ts +1 -0
  141. package/lib/types/gtk-popover-menu-bar.spec.d.ts +1 -0
  142. package/lib/types/gtk-popover-menu.spec.d.ts +1 -0
  143. package/lib/types/gtk-popover-role.spec.d.ts +1 -0
  144. package/lib/types/gtk-popover.spec.d.ts +1 -0
  145. package/lib/types/gtk-print-dialogs.spec.d.ts +1 -0
  146. package/lib/types/gtk-revealer.spec.d.ts +1 -0
  147. package/lib/types/gtk-scale-button.spec.d.ts +1 -0
  148. package/lib/types/gtk-scale.spec.d.ts +1 -0
  149. package/lib/types/gtk-scrollbar.spec.d.ts +1 -0
  150. package/lib/types/gtk-scrolled-window.spec.d.ts +1 -0
  151. package/lib/types/gtk-search-bar.spec.d.ts +1 -0
  152. package/lib/types/gtk-search-entry.spec.d.ts +1 -0
  153. package/lib/types/gtk-separator.spec.d.ts +1 -0
  154. package/lib/types/gtk-spin-button.spec.d.ts +1 -0
  155. package/lib/types/gtk-spinner.spec.d.ts +1 -0
  156. package/lib/types/gtk-stack-sidebar.spec.d.ts +1 -0
  157. package/lib/types/gtk-stack-switcher.spec.d.ts +1 -0
  158. package/lib/types/gtk-stack.spec.d.ts +1 -0
  159. package/lib/types/gtk-text-view.spec.d.ts +1 -0
  160. package/lib/types/gtk-text.spec.d.ts +1 -0
  161. package/lib/types/gtk-toggle-button.spec.d.ts +1 -0
  162. package/lib/types/gtk-tree-expander.spec.d.ts +1 -0
  163. package/lib/types/gtk-video.spec.d.ts +1 -0
  164. package/lib/types/gtk-viewport.spec.d.ts +1 -0
  165. package/lib/types/gtk-window-controls.spec.d.ts +1 -0
  166. package/lib/types/gtk-window-handle.spec.d.ts +1 -0
  167. package/lib/types/gtk-window.spec.d.ts +1 -0
  168. package/lib/types/index.d.ts +4 -1
  169. package/lib/types/list-view-dom.d.ts +44 -0
  170. package/lib/types/namespace/adw.d.ts +12 -0
  171. package/lib/types/namespace/gtk.d.ts +61 -0
  172. package/lib/types/page-setup.d.ts +100 -0
  173. package/lib/types/scroll-shading.d.ts +15 -1
  174. package/lib/types/shared-tree-builder.d.ts +30 -2
  175. package/lib/types/signals.d.ts +4 -0
  176. package/lib/types/styles.generated.d.ts +1 -1
  177. package/lib/types/tab-chip.d.ts +73 -0
  178. package/lib/types/template-classes.d.ts +11 -0
  179. package/package.json +12 -8
  180. package/scss/_action_row.scss +2 -2
  181. package/scss/_application_window.scss +26 -0
  182. package/scss/_aspect_frame.scss +25 -0
  183. package/scss/_bin.scss +26 -0
  184. package/scss/_button.scss +45 -0
  185. package/scss/_calendar.scss +176 -0
  186. package/scss/_center_box.scss +25 -0
  187. package/scss/_checks.scss +6 -1
  188. package/scss/_clamp_scrollable.scss +28 -0
  189. package/scss/_color_button.scss +67 -0
  190. package/scss/_column_view.scss +126 -0
  191. package/scss/_drag_icon.scss +55 -0
  192. package/scss/_drawing.scss +85 -0
  193. package/scss/_emoji_chooser.scss +213 -0
  194. package/scss/_entry.scss +13 -5
  195. package/scss/_entry_row.scss +1 -1
  196. package/scss/_expander.scss +113 -0
  197. package/scss/_fixed.scss +29 -0
  198. package/scss/_flow_box.scss +94 -0
  199. package/scss/_font_button.scss +63 -0
  200. package/scss/_frame.scss +41 -0
  201. package/scss/_grid.scss +19 -0
  202. package/scss/_grid_view.scss +46 -0
  203. package/scss/_gtk_about_dialog.scss +186 -0
  204. package/scss/_headerbar.scss +132 -0
  205. package/scss/_icons.generated.scss +186 -0
  206. package/scss/_inscription.scss +125 -0
  207. package/scss/_level_bar.scss +190 -0
  208. package/scss/_link.scss +82 -0
  209. package/scss/_list_box.scss +88 -0
  210. package/scss/_list_view.scss +96 -0
  211. package/scss/_media_controls.scss +139 -0
  212. package/scss/_overlay.scss +68 -0
  213. package/scss/_paned.scss +130 -0
  214. package/scss/_password_entry.scss +83 -0
  215. package/scss/_picture.scss +80 -0
  216. package/scss/_popover_bin.scss +53 -0
  217. package/scss/_popover_menu_bar.scss +104 -0
  218. package/scss/_preferences_row.scss +51 -0
  219. package/scss/_print_dialogs.scss +328 -0
  220. package/scss/_reset.scss +21 -19
  221. package/scss/_revealer.scss +37 -0
  222. package/scss/_row.scss +20 -5
  223. package/scss/_scale.scss +298 -0
  224. package/scss/_scale_button.scss +167 -0
  225. package/scss/_scrollbar.scss +230 -0
  226. package/scss/_scrolled_window.scss +131 -0
  227. package/scss/_search_bar.scss +118 -0
  228. package/scss/_search_entry.scss +86 -0
  229. package/scss/_separator.scss +44 -0
  230. package/scss/_shortcuts_dialog.scss +100 -0
  231. package/scss/_spin_button.scss +172 -0
  232. package/scss/_spin_row.scss +56 -8
  233. package/scss/_spinner.scss +85 -4
  234. package/scss/_stack.scss +729 -0
  235. package/scss/_switch.scss +13 -5
  236. package/scss/_switch_row.scss +10 -0
  237. package/scss/_tab_bar.scss +317 -0
  238. package/scss/_tab_button.scss +98 -0
  239. package/scss/_tab_overview.scss +483 -0
  240. package/scss/_text.scss +182 -0
  241. package/scss/_theme.scss +1 -1
  242. package/scss/_tree_expander.scss +62 -0
  243. package/scss/_variables.scss +43 -21
  244. package/scss/_video.scss +146 -0
  245. package/scss/_view_switcher_sidebar.scss +112 -0
  246. package/scss/_viewport.scss +50 -0
  247. package/scss/_window.scss +118 -1
  248. package/scss/_window_handle.scss +20 -0
  249. package/scss/adwaita-skin.scss +109 -0
  250. package/src/adw-action-rows.spec.ts +76 -0
  251. package/src/adw-application-window.spec.ts +118 -0
  252. package/src/adw-bin.spec.ts +75 -0
  253. package/src/adw-breakpoint-bin.spec.ts +324 -0
  254. package/src/adw-clamp-scrollable.spec.ts +189 -0
  255. package/src/adw-multi-layout-view.spec.ts +243 -0
  256. package/src/adw-preferences-row.spec.ts +164 -0
  257. package/src/adw-row-state.spec.ts +142 -0
  258. package/src/adw-shortcuts.spec.ts +155 -0
  259. package/src/adw-tab-bar.spec.ts +287 -0
  260. package/src/adw-tab-button.spec.ts +101 -0
  261. package/src/adw-tab-overview.spec.ts +443 -0
  262. package/src/adw-view-switcher-sidebar.spec.ts +281 -0
  263. package/src/adw-window.spec.ts +129 -0
  264. package/src/breakpoints.ts +25 -8
  265. package/src/capabilities.d.mts +3 -0
  266. package/src/capabilities.mjs +18 -0
  267. package/src/connect-lifecycle.spec.ts +12 -0
  268. package/src/construct-vectors.spec.ts +175 -0
  269. package/src/elements/adw-application-window.ts +154 -0
  270. package/src/elements/adw-bin.ts +101 -0
  271. package/src/elements/adw-breakpoint-bin.ts +287 -0
  272. package/src/elements/adw-clamp-scrollable.ts +207 -0
  273. package/src/elements/adw-dialog.ts +19 -0
  274. package/src/elements/adw-layout-slot.ts +95 -0
  275. package/src/elements/adw-multi-layout-view.ts +326 -0
  276. package/src/elements/adw-preferences-row.ts +150 -0
  277. package/src/elements/adw-shortcuts-dialog.ts +52 -0
  278. package/src/elements/adw-shortcuts-item.ts +171 -0
  279. package/src/elements/adw-shortcuts-section.ts +86 -0
  280. package/src/elements/adw-sidebar.ts +42 -3
  281. package/src/elements/adw-switch-row.ts +28 -1
  282. package/src/elements/adw-tab-bar.ts +529 -0
  283. package/src/elements/adw-tab-button.ts +194 -0
  284. package/src/elements/adw-tab-overview.ts +1011 -0
  285. package/src/elements/adw-tab-view.ts +66 -145
  286. package/src/elements/adw-toggle-group.ts +20 -5
  287. package/src/elements/adw-view-stack.ts +18 -0
  288. package/src/elements/adw-view-switcher-sidebar.ts +332 -0
  289. package/src/elements/adw-window.ts +185 -11
  290. package/src/elements/checks.ts +35 -8
  291. package/src/elements/gtk-about-dialog.ts +811 -0
  292. package/src/elements/gtk-application-window.ts +108 -0
  293. package/src/elements/gtk-aspect-frame.ts +160 -0
  294. package/src/elements/gtk-button.ts +12 -0
  295. package/src/elements/gtk-calendar.ts +757 -0
  296. package/src/elements/gtk-center-box.ts +184 -0
  297. package/src/elements/gtk-color-dialog-button.ts +278 -0
  298. package/src/elements/gtk-column-view.ts +320 -0
  299. package/src/elements/gtk-drag-icon.ts +257 -0
  300. package/src/elements/gtk-drawing-area.ts +303 -0
  301. package/src/elements/gtk-editable-label.ts +204 -0
  302. package/src/elements/gtk-emoji-chooser.ts +542 -0
  303. package/src/elements/gtk-entry.ts +39 -5
  304. package/src/elements/gtk-expander.ts +301 -0
  305. package/src/elements/gtk-fixed.ts +99 -0
  306. package/src/elements/gtk-flow-box-child.ts +68 -0
  307. package/src/elements/gtk-flow-box.ts +480 -0
  308. package/src/elements/gtk-font-dialog-button.ts +432 -0
  309. package/src/elements/gtk-frame.ts +174 -0
  310. package/src/elements/gtk-gl-area.ts +476 -0
  311. package/src/elements/gtk-graphics-offload.ts +130 -0
  312. package/src/elements/gtk-grid-view.ts +168 -0
  313. package/src/elements/gtk-grid.ts +127 -0
  314. package/src/elements/gtk-header-bar.ts +273 -0
  315. package/src/elements/gtk-inscription.ts +387 -0
  316. package/src/elements/gtk-label.ts +19 -11
  317. package/src/elements/gtk-level-bar.ts +323 -0
  318. package/src/elements/gtk-link-button.ts +138 -0
  319. package/src/elements/gtk-list-box-row.ts +195 -0
  320. package/src/elements/gtk-list-box.ts +413 -0
  321. package/src/elements/gtk-list-view.ts +370 -0
  322. package/src/elements/gtk-media-controls.ts +509 -0
  323. package/src/elements/gtk-notebook.ts +664 -0
  324. package/src/elements/gtk-overlay.ts +149 -0
  325. package/src/elements/gtk-page-setup-unix-dialog.ts +420 -0
  326. package/src/elements/gtk-paned.ts +597 -0
  327. package/src/elements/gtk-password-entry.ts +170 -0
  328. package/src/elements/gtk-picture.ts +254 -0
  329. package/src/elements/gtk-popover-bin.ts +332 -0
  330. package/src/elements/gtk-popover-menu-bar.ts +355 -0
  331. package/src/elements/gtk-popover-menu.ts +339 -0
  332. package/src/elements/gtk-popover.ts +92 -10
  333. package/src/elements/gtk-print-unix-dialog.ts +989 -0
  334. package/src/elements/gtk-progress-bar.ts +24 -11
  335. package/src/elements/gtk-revealer.ts +406 -0
  336. package/src/elements/gtk-scale-button.ts +358 -0
  337. package/src/elements/gtk-scale.ts +492 -0
  338. package/src/elements/gtk-scrollbar.ts +528 -0
  339. package/src/elements/gtk-scrolled-window.ts +642 -0
  340. package/src/elements/gtk-search-bar.ts +200 -0
  341. package/src/elements/gtk-search-entry.ts +172 -0
  342. package/src/elements/gtk-separator.ts +52 -0
  343. package/src/elements/gtk-spin-button.ts +475 -0
  344. package/src/elements/gtk-spinner.ts +126 -0
  345. package/src/elements/gtk-stack-sidebar.ts +212 -0
  346. package/src/elements/gtk-stack-switcher.ts +279 -0
  347. package/src/elements/gtk-stack.ts +748 -0
  348. package/src/elements/gtk-switch.ts +52 -19
  349. package/src/elements/gtk-text-view.ts +290 -0
  350. package/src/elements/gtk-text.ts +219 -0
  351. package/src/elements/gtk-toggle-button.ts +239 -0
  352. package/src/elements/gtk-tree-expander.ts +243 -0
  353. package/src/elements/gtk-video.ts +423 -0
  354. package/src/elements/gtk-viewport.ts +277 -0
  355. package/src/elements/gtk-window-controls.ts +341 -0
  356. package/src/elements/gtk-window-handle.ts +221 -0
  357. package/src/elements/gtk-window.ts +366 -0
  358. package/src/elements/justification.ts +24 -0
  359. package/src/elements/popover-menu.ts +24 -0
  360. package/src/elements/roving-focus.ts +53 -21
  361. package/src/emoji-data.ts +577 -0
  362. package/src/entry-rows.spec.ts +31 -0
  363. package/src/font-description.ts +238 -0
  364. package/src/gtk-about-dialog.spec.ts +250 -0
  365. package/src/gtk-action-bar.spec.ts +89 -0
  366. package/src/gtk-application-window.spec.ts +107 -0
  367. package/src/gtk-aspect-frame.spec.ts +114 -0
  368. package/src/gtk-calendar.spec.ts +589 -0
  369. package/src/gtk-center-box.spec.ts +125 -0
  370. package/src/gtk-color-dialog-button.spec.ts +141 -0
  371. package/src/gtk-column-view.spec.ts +154 -0
  372. package/src/gtk-drag-icon.spec.ts +190 -0
  373. package/src/gtk-drawing-area.spec.ts +211 -0
  374. package/src/gtk-editable-label.spec.ts +190 -0
  375. package/src/gtk-emoji-chooser.spec.ts +173 -0
  376. package/src/gtk-entry.spec.ts +23 -0
  377. package/src/gtk-expander.spec.ts +170 -0
  378. package/src/gtk-fixed.spec.ts +97 -0
  379. package/src/gtk-flow-box.spec.ts +289 -0
  380. package/src/gtk-font-dialog-button.spec.ts +166 -0
  381. package/src/gtk-frame.spec.ts +119 -0
  382. package/src/gtk-gl-area.spec.ts +361 -0
  383. package/src/gtk-graphics-offload.spec.ts +119 -0
  384. package/src/gtk-grid-view.spec.ts +106 -0
  385. package/src/gtk-grid.spec.ts +122 -0
  386. package/src/gtk-header-bar.spec.ts +202 -0
  387. package/src/gtk-inscription.spec.ts +235 -0
  388. package/src/gtk-level-bar.spec.ts +199 -0
  389. package/src/gtk-link-button.spec.ts +117 -0
  390. package/src/gtk-list-box.spec.ts +422 -0
  391. package/src/gtk-list-view.spec.ts +203 -0
  392. package/src/gtk-media-controls.spec.ts +295 -0
  393. package/src/gtk-notebook.spec.ts +546 -0
  394. package/src/gtk-overlay.spec.ts +141 -0
  395. package/src/gtk-paned.spec.ts +267 -0
  396. package/src/gtk-password-entry.spec.ts +150 -0
  397. package/src/gtk-picture.spec.ts +172 -0
  398. package/src/gtk-popover-bin.spec.ts +229 -0
  399. package/src/gtk-popover-menu-bar.spec.ts +296 -0
  400. package/src/gtk-popover-menu.spec.ts +261 -0
  401. package/src/gtk-popover-role.spec.ts +138 -0
  402. package/src/gtk-popover.spec.ts +264 -0
  403. package/src/gtk-print-dialogs.spec.ts +251 -0
  404. package/src/gtk-revealer.spec.ts +164 -0
  405. package/src/gtk-scale-button.spec.ts +218 -0
  406. package/src/gtk-scale.spec.ts +213 -0
  407. package/src/gtk-scrollbar.spec.ts +244 -0
  408. package/src/gtk-scrolled-window.spec.ts +262 -0
  409. package/src/gtk-search-bar.spec.ts +179 -0
  410. package/src/gtk-search-entry.spec.ts +142 -0
  411. package/src/gtk-separator.spec.ts +74 -0
  412. package/src/gtk-spin-button.spec.ts +186 -0
  413. package/src/gtk-spinner.spec.ts +130 -0
  414. package/src/gtk-stack-sidebar.spec.ts +217 -0
  415. package/src/gtk-stack-switcher.spec.ts +249 -0
  416. package/src/gtk-stack.spec.ts +432 -0
  417. package/src/gtk-text-view.spec.ts +189 -0
  418. package/src/gtk-text.spec.ts +163 -0
  419. package/src/gtk-toggle-button.spec.ts +186 -0
  420. package/src/gtk-tree-expander.spec.ts +201 -0
  421. package/src/gtk-video.spec.ts +240 -0
  422. package/src/gtk-viewport.spec.ts +140 -0
  423. package/src/gtk-window-controls.spec.ts +232 -0
  424. package/src/gtk-window-handle.spec.ts +158 -0
  425. package/src/gtk-window.spec.ts +190 -0
  426. package/src/index.ts +9 -1
  427. package/src/keyboard-operable.spec.ts +33 -1
  428. package/src/list-view-dom.ts +121 -0
  429. package/src/namespace/adw.ts +12 -0
  430. package/src/namespace/gtk.ts +61 -0
  431. package/src/page-setup.ts +222 -0
  432. package/src/scroll-shading.ts +18 -1
  433. package/src/shared-tree-builder.ts +263 -21
  434. package/src/signals.ts +15 -0
  435. package/src/source-view/theme.ts +11 -5
  436. package/src/style-classes.spec.ts +9 -8
  437. package/src/styles.generated.ts +1 -1
  438. package/src/tab-chip.ts +200 -0
  439. package/src/template-classes.ts +45 -0
  440. package/src/test.browser.mts +152 -0
@@ -0,0 +1,296 @@
1
+ // `<gtk-popover-menu-bar>` — the widget whose whole behaviour is `set_active_item`
2
+ // (gtkpopovermenubar.c:126-163). Three outcomes hang off two booleans there, and this spec
3
+ // drives each of them separately, because merging them is how a menubar comes to open its
4
+ // menus on HOVER — the one thing that distinguishes it from a row of menu buttons.
5
+
6
+ import { describe, expect, it } from '@gjsify/unit';
7
+ import type { AdwMenuInput } from '@gjsify/adwaita-core';
8
+
9
+ interface MenuBarElement extends HTMLElement {
10
+ menuModel: AdwMenuInput;
11
+ actions: Record<string, { enabled?: boolean; state?: string }> | null;
12
+ activeItem: HTMLElement | null;
13
+ selectFirst(): void;
14
+ popup(): void;
15
+ }
16
+
17
+ /** Three submenus, the shape GTK builds one item for (`tracker_insert`, :422-468). */
18
+ const MENU = [
19
+ { label: 'File', submenu: [{ label: 'New' }, { label: 'Open' }] },
20
+ { label: 'Edit', submenu: [{ label: 'Cut' }] },
21
+ { label: 'View', submenu: [{ label: 'Zoom In' }] },
22
+ ];
23
+
24
+ function mount(json?: string): MenuBarElement {
25
+ const el = document.createElement('gtk-popover-menu-bar') as MenuBarElement;
26
+ if (json !== undefined) el.setAttribute('menu-model', json);
27
+ document.body.appendChild(el);
28
+ return el;
29
+ }
30
+
31
+ const items = (el: HTMLElement): HTMLElement[] => [...el.querySelectorAll<HTMLElement>('.adw-popover-menu-bar-item')];
32
+ const buttons = (el: HTMLElement): HTMLButtonElement[] => [
33
+ ...el.querySelectorAll<HTMLButtonElement>('.adw-popover-menu-bar-button'),
34
+ ];
35
+ /** Each item's popover, typed with the surface the element drives. */
36
+ interface PopoverSurface extends HTMLElement {
37
+ open: boolean;
38
+ popdown(): void;
39
+ }
40
+
41
+ const popovers = (el: HTMLElement): PopoverSurface[] =>
42
+ [...el.querySelectorAll<HTMLElement>('gtk-popover-menu')] as unknown as PopoverSurface[];
43
+
44
+ /** `item_enter_cb` — a motion ENTER, which selects WITHOUT opening. */
45
+ function hover(item: HTMLElement): void {
46
+ item.dispatchEvent(new PointerEvent('pointerenter', { bubbles: false }));
47
+ }
48
+
49
+ function unmountAll(): void {
50
+ for (const el of Array.from(document.querySelectorAll('gtk-popover-menu-bar'))) el.remove();
51
+ }
52
+
53
+ export const GtkPopoverMenuBarTest = async () => {
54
+ await describe('<gtk-popover-menu-bar> the items', async () => {
55
+ await it('builds one item per TOPLEVEL SUBMENU, each with a popover under it', () => {
56
+ const el = mount();
57
+ el.menuModel = MENU;
58
+ expect(items(el).length).toBe(3);
59
+ expect(buttons(el).map((b) => b.textContent)).toStrictEqual(['File', 'Edit', 'View']);
60
+ expect(popovers(el).length).toBe(3);
61
+ // `role` is set on the BAR and on the ITEMS, never on the wrapper
62
+ // (gtkpopovermenubar.c:641, :347-349, :55-57).
63
+ expect(el.getAttribute('role')).toBe('menubar');
64
+ expect(buttons(el).every((b) => b.getAttribute('role') === 'menuitem')).toBe(true);
65
+ expect(items(el).every((item) => item.getAttribute('role') === 'none')).toBe(true);
66
+ unmountAll();
67
+ });
68
+
69
+ await it('pins each popover to the bottom, start-aligned and arrow-less', () => {
70
+ const el = mount();
71
+ el.menuModel = MENU;
72
+ // `gtk_popover_set_position (popover, GTK_POS_BOTTOM)`,
73
+ // `set_has_arrow (FALSE)` and `set_halign (START)` (gtkpopovermenubar.c:435-437).
74
+ for (const popover of popovers(el)) {
75
+ expect(popover.getAttribute('position')).toBe('bottom');
76
+ expect(popover.getAttribute('align')).toBe('start');
77
+ expect(popover.hasAttribute('has-arrow')).toBe(false);
78
+ }
79
+ unmountAll();
80
+ });
81
+
82
+ await it('renders the submenu’s own items, not the whole model', () => {
83
+ const el = mount();
84
+ el.menuModel = MENU;
85
+ const first = popovers(el)[0];
86
+ expect([...first.querySelectorAll('.adw-popover-menu-item-label')].map((n) => n.textContent)).toStrictEqual(
87
+ ['New', 'Open'],
88
+ );
89
+ unmountAll();
90
+ });
91
+
92
+ await it('gives every item HAS_POPUP and a CONTROLS relation', () => {
93
+ const el = mount();
94
+ el.menuModel = MENU;
95
+ for (const button of buttons(el)) {
96
+ expect(button.getAttribute('aria-haspopup')).toBe('menu');
97
+ expect(button.getAttribute('aria-expanded')).toBe('false');
98
+ // `gtk_accessible_update_relation (…, LABELLED_BY, label, CONTROLS, popover)`.
99
+ expect(button.getAttribute('aria-controls')).toBe('popover');
100
+ }
101
+ unmountAll();
102
+ });
103
+ });
104
+
105
+ await describe('<gtk-popover-menu-bar> set_active_item', async () => {
106
+ await it('a CLICK opens; a HOVER only selects', () => {
107
+ const el = mount();
108
+ el.menuModel = MENU;
109
+ const [file] = items(el);
110
+
111
+ hover(file);
112
+ // `item_enter_cb` passes FALSE for `popup` (gtkpopovermenubar.c:178-189), so the
113
+ // popover stays down while the item is the selected one.
114
+ expect(el.activeItem).toBe(buttons(el)[0]);
115
+ expect(popovers(el)[0].hasAttribute('open')).toBe(false);
116
+ // `.active` belongs to the item whose POPOVER IS OPEN (gtkpopovermenubar.c:50-51).
117
+ expect(file.classList.contains('active')).toBe(false);
118
+
119
+ buttons(el)[0].click();
120
+ // `clicked_cb` passes TRUE (:165-176).
121
+ expect(popovers(el)[0].open).toBe(true);
122
+ expect(file.classList.contains('active')).toBe(true);
123
+ unmountAll();
124
+ });
125
+
126
+ await it('opening a second item closes the first, because it was already mapped', () => {
127
+ const el = mount();
128
+ el.menuModel = MENU;
129
+ buttons(el)[0].click();
130
+ expect(popovers(el)[0].open).toBe(true);
131
+ // `changed` alone does not close it; `was_popup && changed` does (:137-139).
132
+ buttons(el)[1].click();
133
+ expect(popovers(el)[0].open).toBe(false);
134
+ expect(popovers(el)[1].open).toBe(true);
135
+ expect(items(el)[1].classList.contains('active')).toBe(true);
136
+ expect(items(el)[0].classList.contains('active')).toBe(false);
137
+ unmountAll();
138
+ });
139
+
140
+ await it('hovering away from an OPEN item carries the menu along', () => {
141
+ const el = mount();
142
+ el.menuModel = MENU;
143
+ buttons(el)[0].click();
144
+ // `if (popup || (was_popup && changed)) open_submenu (…)` (:153-155) — a pointer
145
+ // moving along the bar keeps the menu open and follows it.
146
+ hover(items(el)[2]);
147
+ expect(popovers(el)[2].open).toBe(true);
148
+ expect(popovers(el)[0].open).toBe(false);
149
+ unmountAll();
150
+ });
151
+
152
+ await it('leaving the bar clears the selection but not an open menu', () => {
153
+ const el = mount();
154
+ el.menuModel = MENU;
155
+ hover(items(el)[1]);
156
+ el.dispatchEvent(new PointerEvent('pointerleave'));
157
+ // `bar_leave_cb` clears only when the item's popover is NOT mapped
158
+ // (gtkpopovermenubar.c:203-214) — the pointer may be on the menu, not the bar.
159
+ expect(el.activeItem).toBeNull();
160
+
161
+ buttons(el)[1].click();
162
+ expect(popovers(el)[1].open).toBe(true);
163
+ el.dispatchEvent(new PointerEvent('pointerleave'));
164
+ expect(el.activeItem).not.toBeNull();
165
+ unmountAll();
166
+ });
167
+
168
+ await it('unmapping a popover clears its item, as popover_unmap does', () => {
169
+ const el = mount();
170
+ el.menuModel = MENU;
171
+ buttons(el)[0].click();
172
+ expect(el.activeItem).not.toBeNull();
173
+ popovers(el)[0].popdown();
174
+ // `popover_unmap` → `set_active_item (bar, NULL, FALSE)` (:389-396).
175
+ expect(el.activeItem).toBeNull();
176
+ expect(buttons(el)[0].getAttribute('aria-expanded')).toBe('false');
177
+ unmountAll();
178
+ });
179
+
180
+ await it('selectFirst activates the FIRST item and opens it', () => {
181
+ const el = mount();
182
+ el.menuModel = MENU;
183
+ el.selectFirst();
184
+ expect(el.activeItem).toBe(buttons(el)[0]);
185
+ expect(popovers(el)[0].open).toBe(true);
186
+ // `gtk_popover_menu_bar_select_first` (:761-767).
187
+ unmountAll();
188
+ });
189
+ });
190
+
191
+ await describe('<gtk-popover-menu-bar> the keys', async () => {
192
+ await it('Left and Right wrap at both ends', () => {
193
+ const el = mount();
194
+ el.menuModel = MENU;
195
+ const send = (key: string) => el.dispatchEvent(new KeyboardEvent('keydown', { key, bubbles: true }));
196
+
197
+ // No active item, so there is no sibling: RIGHT goes to the FIRST child and LEFT
198
+ // to the LAST (gtkpopovermenubar.c:232-252).
199
+ send('ArrowRight');
200
+ expect(el.activeItem).toBe(buttons(el)[0]);
201
+ send('ArrowRight');
202
+ expect(el.activeItem).toBe(buttons(el)[1]);
203
+ send('ArrowRight');
204
+ expect(el.activeItem).toBe(buttons(el)[2]);
205
+ send('ArrowRight');
206
+ expect(el.activeItem).toBe(buttons(el)[0]);
207
+ send('ArrowLeft');
208
+ expect(el.activeItem).toBe(buttons(el)[2]);
209
+ unmountAll();
210
+ });
211
+
212
+ await it('an OPEN popover keeps the keys — the bar forwards into it first', () => {
213
+ const el = mount();
214
+ el.menuModel = MENU;
215
+ buttons(el)[0].click();
216
+ const before = el.activeItem;
217
+ el.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowRight', bubbles: true }));
218
+ // `gtk_popover_menu_bar_focus` tries `gtk_widget_child_focus` on the open
219
+ // submenu BEFORE moving between items (:224-231), so LEFT/RIGHT belongs to the
220
+ // menu and not to the bar.
221
+ expect(el.activeItem).toBe(before);
222
+ unmountAll();
223
+ });
224
+
225
+ await it('an item focused by a key opens its menu when focus is already in the bar', async () => {
226
+ const el = mount();
227
+ el.menuModel = MENU;
228
+ buttons(el)[0].focus();
229
+ const send = (key: string) => el.dispatchEvent(new KeyboardEvent('keydown', { key, bubbles: true }));
230
+ send('ArrowRight'); // no active item yet, so RIGHT is the FIRST child
231
+ send('ArrowRight'); // and from there the next one
232
+ // `else if (changed && (state & GTK_STATE_FLAG_FOCUS_WITHIN)) grab_focus` — and
233
+ // focus MOVED, because a `.selected` class alone does not take focus.
234
+ expect(el.activeItem).toBe(buttons(el)[1]);
235
+ expect(document.activeElement).toBe(buttons(el)[1]);
236
+ unmountAll();
237
+ });
238
+ });
239
+
240
+ await describe('<gtk-popover-menu-bar> activation', async () => {
241
+ await it('choosing a row closes that popover and reports the WHOLE-model path', () => {
242
+ const el = mount();
243
+ el.menuModel = MENU;
244
+ buttons(el)[0].click();
245
+ let detail: { id: string; label: string; path: number[] } | null = null;
246
+ el.addEventListener('menu-item-activated', (event) => {
247
+ detail = (event as CustomEvent<{ id: string; label: string; path: number[] }>).detail;
248
+ });
249
+ const rows = [...popovers(el)[0].querySelectorAll<HTMLButtonElement>('.adw-popover-item')];
250
+ rows[1].click(); // `Open`, inside `File`
251
+ // `File ▸ Open` is `[0, 1]` in the bar's model, not `[1]` in the submenu's.
252
+ expect(detail).toStrictEqual({ id: 'Open', label: 'Open', path: [0, 1] });
253
+ expect(popovers(el)[0].open).toBe(false);
254
+ expect(document.activeElement).toBe(buttons(el)[0]);
255
+ unmountAll();
256
+ });
257
+ });
258
+
259
+ await describe('<gtk-popover-menu-bar> refuses what it cannot use', async () => {
260
+ await it('a toplevel item is warned about and NOT drawn, as tracker_insert does', () => {
261
+ const warnings: unknown[] = [];
262
+ const real = console.warn;
263
+ console.warn = (...args: unknown[]) => warnings.push(args.join(' '));
264
+ try {
265
+ const el = mount('[{"label":"Not a menu"},{"label":"File","submenu":[{"label":"New"}]}]');
266
+ // "Don't know how to handle this item" (gtkpopovermenubar.c:466) — and
267
+ // `g_warning` is RECOVERABLE, so the submenu beside it still works.
268
+ expect(warnings.join('\n')).toContain("Don't know how to handle this item");
269
+ expect(buttons(el).map((b) => b.textContent)).toStrictEqual(['File']);
270
+ unmountAll();
271
+ } finally {
272
+ console.warn = real;
273
+ }
274
+ });
275
+
276
+ await it('a `custom` item anywhere in the model refuses the whole menu', () => {
277
+ const errors: unknown[] = [];
278
+ const real = console.error;
279
+ console.error = (...args: unknown[]) => errors.push(args.join(' '));
280
+ try {
281
+ const el = mount('[{"label":"File","submenu":[{"label":"Zoom","custom":"zoom"}]}]');
282
+ expect(buttons(el).length).toBe(0);
283
+ expect(errors.join('\n')).toContain('zoom');
284
+ unmountAll();
285
+ } finally {
286
+ console.error = real;
287
+ }
288
+ });
289
+
290
+ await it('a malformed attribute leaves the bar empty rather than failing to upgrade', () => {
291
+ const el = mount('not json at all');
292
+ expect(buttons(el).length).toBe(0);
293
+ unmountAll();
294
+ });
295
+ });
296
+ };
@@ -0,0 +1,261 @@
1
+ // `<gtk-popover-menu>` — the menu-model-driven popover. What is worth pinning here is what
2
+ // `gtkpopovermenu.c` adds ON TOP of `<gtk-popover>`: the page stack (`visible-submenu`,
3
+ // `gtk_stack_set_visible_child_name`, gtkpopovermenu.c:755-757), the `main` way back, and
4
+ // the activation rule that closes the popover and gives focus to the anchor.
5
+ //
6
+ // The rows themselves are `PopoverMenuView`, ratcheted through `MENU_*_VECTORS` in
7
+ // `<gtk-menu-button>`'s spec against the same core tables — a third copy of those vectors
8
+ // would be a third place to forget.
9
+
10
+ import { describe, expect, it } from '@gjsify/unit';
11
+ import { MENU_REFUSAL_VECTORS } from '@gjsify/adwaita-core/conformance';
12
+ import { flattenMenu } from '@gjsify/adwaita-core';
13
+ import type { AdwMenuInput, AdwMenuModel } from '@gjsify/adwaita-core';
14
+
15
+ interface PopoverMenuElement extends HTMLElement {
16
+ menuModel: AdwMenuInput | AdwMenuModel;
17
+ actions: Record<string, { enabled?: boolean; state?: string }> | null;
18
+ visibleSubmenu: string;
19
+ // The three below are `GtkPopover`'s own — the reason this spec can say "inherits the
20
+ // state machine rather than copying it" and have the compiler agree.
21
+ open: boolean;
22
+ autohide: boolean;
23
+ anchor: HTMLElement | null;
24
+ openSubmenu(name: string): void;
25
+ closeSubmenus(): void;
26
+ popup(): void;
27
+ popdown(): void;
28
+ }
29
+
30
+ const NESTED = [{ label: 'New' }, { label: 'More', submenu: [{ label: 'Rename' }, { label: 'Duplicate' }] }];
31
+
32
+ function mount(json?: string): PopoverMenuElement {
33
+ const el = document.createElement('gtk-popover-menu') as PopoverMenuElement;
34
+ if (json !== undefined) el.setAttribute('menu-model', json);
35
+ document.body.appendChild(el);
36
+ return el;
37
+ }
38
+
39
+ function labels(el: HTMLElement): string[] {
40
+ return [...el.querySelectorAll('.adw-popover-item')].map(
41
+ (row) => row.querySelector('.adw-popover-menu-item-label')?.textContent ?? '',
42
+ );
43
+ }
44
+
45
+ function unmountAll(): void {
46
+ for (const el of Array.from(document.querySelectorAll('gtk-popover-menu'))) el.remove();
47
+ }
48
+
49
+ export const GtkPopoverMenuTest = async () => {
50
+ await describe('<gtk-popover-menu> the surface', async () => {
51
+ await it('carries the .menu variant and the menu role, as GtkPopoverMenu does', () => {
52
+ const el = mount();
53
+ // gtkpopovermenu.c:152-154 — "It is one of the cases that add a `.menu` style
54
+ // class to the main `popover` node", and :688-692 for the accessible role.
55
+ expect(el.classList.contains('adw-popover-menu')).toBe(true);
56
+ expect(el.getAttribute('menu')).toBe('');
57
+ expect(el.getAttribute('role')).toBe('menu');
58
+ unmountAll();
59
+ });
60
+
61
+ await it('inherits the popover state machine rather than copying it', () => {
62
+ const el = mount();
63
+ el.open = true;
64
+ expect(el.hasAttribute('open')).toBe(true);
65
+ expect(el.hidden).toBe(false);
66
+ el.popdown();
67
+ expect(el.open).toBe(false);
68
+ // `autohide` is `GtkPopover`'s, and `GtkPopoverMenu` is a subclass of it —
69
+ // which is why `gtk_popover_menu_new` spells `autohide = TRUE` explicitly
70
+ // (gtkpopovermenu.c:729-731) even though it is already the default.
71
+ expect(el.autohide).toBe(true);
72
+ unmountAll();
73
+ });
74
+ });
75
+
76
+ await describe('<gtk-popover-menu> the page stack', async () => {
77
+ await it('renders the model and reports the root page as `main`', () => {
78
+ const el = mount();
79
+ el.menuModel = NESTED;
80
+ expect(labels(el)).toStrictEqual(['New', 'More']);
81
+ expect(el.visibleSubmenu).toBe('main');
82
+ unmountAll();
83
+ });
84
+
85
+ await it('opens a submenu by name and swaps the page', () => {
86
+ const el = mount();
87
+ el.menuModel = NESTED;
88
+ el.openSubmenu('More');
89
+ // A submenu's page is the back row plus its own items — the submenu's contents
90
+ // are never inlined into the parent.
91
+ expect(labels(el)).toStrictEqual(['More', 'Rename', 'Duplicate']);
92
+ expect(el.visibleSubmenu).toBe('More');
93
+ unmountAll();
94
+ });
95
+
96
+ await it('`main` goes back, and so does closeSubmenus', () => {
97
+ const el = mount();
98
+ el.menuModel = NESTED;
99
+ el.openSubmenu('More');
100
+ el.openSubmenu('main');
101
+ expect(labels(el)).toStrictEqual(['New', 'More']);
102
+ el.openSubmenu('More');
103
+ // `gtk_popover_menu_close_submenus` leaves ONE level (gtkpopovermenu.c:223-233).
104
+ el.closeSubmenus();
105
+ expect(el.visibleSubmenu).toBe('main');
106
+ expect(labels(el)).toStrictEqual(['New', 'More']);
107
+ unmountAll();
108
+ });
109
+
110
+ await it('a name no submenu carries leaves the open page alone', () => {
111
+ const el = mount();
112
+ el.menuModel = NESTED;
113
+ el.openSubmenu('More');
114
+ // `gtk_stack_set_visible_child_name` on a child that is not there is a no-op in
115
+ // GTK, so the current page must survive rather than blank the surface.
116
+ el.openSubmenu('Nope');
117
+ expect(el.visibleSubmenu).toBe('More');
118
+ expect(labels(el)).toStrictEqual(['More', 'Rename', 'Duplicate']);
119
+ unmountAll();
120
+ });
121
+
122
+ await it('`visible-submenu` as an ATTRIBUTE navigates, and notifies once per page', () => {
123
+ const el = mount();
124
+ el.menuModel = NESTED;
125
+ const seen: string[] = [];
126
+ el.addEventListener('notify::visible-submenu', (event) => {
127
+ seen.push((event as CustomEvent<{ visibleSubmenu: string }>).detail.visibleSubmenu);
128
+ });
129
+ el.setAttribute('visible-submenu', 'More');
130
+ expect(el.visibleSubmenu).toBe('More');
131
+ // ONE notify per real change: the attribute path is answered by navigation, and
132
+ // navigation writes the attribute back — a second notify would report the same
133
+ // page twice.
134
+ expect(seen).toStrictEqual(['More']);
135
+ unmountAll();
136
+ });
137
+
138
+ await it('a CHANGED model drops the open page', () => {
139
+ const el = mount();
140
+ el.menuModel = NESTED;
141
+ el.openSubmenu('More');
142
+ el.menuModel = [{ label: 'Only' }];
143
+ // The submenu the page named is gone; staying on it would leave a back row and
144
+ // no items behind it.
145
+ expect(el.visibleSubmenu).toBe('main');
146
+ expect(labels(el)).toStrictEqual(['Only']);
147
+ unmountAll();
148
+ });
149
+
150
+ await it('an UNCHANGED model keeps the open page', () => {
151
+ const el = mount();
152
+ el.menuModel = NESTED;
153
+ el.openSubmenu('More');
154
+ el.menuModel = JSON.parse(JSON.stringify(NESTED)) as AdwMenuInput;
155
+ expect(el.visibleSubmenu).toBe('More');
156
+ unmountAll();
157
+ });
158
+
159
+ await it('closing the popover returns to the root page', async () => {
160
+ const el = mount();
161
+ el.menuModel = NESTED;
162
+ el.openSubmenu('More');
163
+ el.open = true;
164
+ el.open = false;
165
+ el.open = true;
166
+ // `gtk_popover_menu_show` clears the open submenu (gtkpopovermenu.c:573-579).
167
+ expect(el.visibleSubmenu).toBe('main');
168
+ unmountAll();
169
+ });
170
+ });
171
+
172
+ await describe('<gtk-popover-menu> activation closes it', async () => {
173
+ await it('choosing an item pops down, focuses the anchor and reports the path', () => {
174
+ const anchor = document.createElement('button');
175
+ const el = document.createElement('gtk-popover-menu') as PopoverMenuElement;
176
+ anchor.appendChild(el);
177
+ document.body.appendChild(anchor);
178
+ el.menuModel = NESTED;
179
+ el.openSubmenu('More');
180
+ el.anchor = anchor;
181
+ el.open = true;
182
+
183
+ let detail: { id: string; label: string; path: number[] } | null = null;
184
+ el.addEventListener('menu-item-activated', (event) => {
185
+ detail = (event as CustomEvent<{ id: string; label: string; path: number[] }>).detail;
186
+ });
187
+ const rows = [...el.querySelectorAll<HTMLButtonElement>('.adw-popover-item')];
188
+ rows[1].click(); // `Rename`, inside the submenu
189
+
190
+ // A menu cannot be left open by choosing from it, and the reader is handed back
191
+ // to whatever opened it.
192
+ expect(el.open).toBe(false);
193
+ expect(document.activeElement).toBe(anchor);
194
+ expect(detail).toStrictEqual({ id: 'Rename', label: 'Rename', path: [1, 0] });
195
+ anchor.remove();
196
+ });
197
+
198
+ await it('a disabled row activates nothing', () => {
199
+ const el = mount();
200
+ el.menuModel = [{ label: 'Quit', action: 'app.quit' }];
201
+ el.actions = { 'app.quit': { enabled: false } };
202
+ let fired = false;
203
+ el.addEventListener('menu-item-activated', () => {
204
+ fired = true;
205
+ });
206
+ (el.querySelector('.adw-popover-item') as HTMLButtonElement).click();
207
+ expect(fired).toBe(false);
208
+ unmountAll();
209
+ });
210
+ });
211
+
212
+ await describe('<gtk-popover-menu> the menu attribute', async () => {
213
+ await it('reads the JSON and draws the same rows `flattenMenu` names', () => {
214
+ const el = mount('[{"label":"New"},{"section":[{"label":"Cut"}],"label":"Edit"}]');
215
+ const model: AdwMenuModel = [
216
+ { kind: 'item', label: 'New' },
217
+ { kind: 'section', label: 'Edit', items: [{ kind: 'item', label: 'Cut' }] },
218
+ ];
219
+ expect(labels(el)).toStrictEqual(flattenMenu(model).map((row) => row.node.label));
220
+ unmountAll();
221
+ });
222
+
223
+ await it('a malformed attribute is not a refusal — a typo may not stop the upgrade', () => {
224
+ const el = mount('not json at all');
225
+ expect(labels(el)).toStrictEqual([]);
226
+ unmountAll();
227
+ });
228
+
229
+ await it('the ATTRIBUTE path refuses a `custom` item without throwing', () => {
230
+ const errors: unknown[] = [];
231
+ const real = console.error;
232
+ console.error = (...args: unknown[]) => errors.push(args.join(' '));
233
+ try {
234
+ const el = mount('[{"label":"Zoom","custom":"zoom-control"}]');
235
+ // A throw from `connectedCallback` reaches nobody — it is an uncaught page
236
+ // error — so markup refuses the menu instead, which is visible.
237
+ expect(labels(el)).toStrictEqual([]);
238
+ expect(errors.join('\n')).toContain('zoom-control');
239
+ el.remove();
240
+ } finally {
241
+ console.error = real;
242
+ }
243
+ });
244
+
245
+ await it('the PROPERTY path refuses a `custom` item loudly', () => {
246
+ for (const { model, paths, rule } of MENU_REFUSAL_VECTORS) {
247
+ const el = mount();
248
+ if (paths.length > 0) {
249
+ expect(() => {
250
+ el.menuModel = model;
251
+ }).toThrow('adwaita-web');
252
+ } else {
253
+ el.menuModel = model;
254
+ expect(labels(el)).toStrictEqual(flattenMenu(model).map((row) => row.node.label));
255
+ }
256
+ expect(rule.length > 0).toBe(true);
257
+ el.remove();
258
+ }
259
+ });
260
+ });
261
+ };