@gjsify/adwaita-web 0.54.0 → 0.56.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 (479) hide show
  1. package/README.md +8 -0
  2. package/dist/adwaita-web.css +8146 -3011
  3. package/dist/adwaita-web.css.map +1 -1
  4. package/lib/types/adw-application-window.spec.d.ts +1 -0
  5. package/lib/types/adw-bin.spec.d.ts +1 -0
  6. package/lib/types/adw-breakpoint-bin.spec.d.ts +1 -0
  7. package/lib/types/adw-clamp-scrollable.spec.d.ts +1 -0
  8. package/lib/types/adw-multi-layout-view.spec.d.ts +1 -0
  9. package/lib/types/adw-preferences-row.spec.d.ts +1 -0
  10. package/lib/types/adw-shortcuts.spec.d.ts +1 -0
  11. package/lib/types/adw-status-page.spec.d.ts +1 -0
  12. package/lib/types/adw-tab-bar.spec.d.ts +1 -0
  13. package/lib/types/adw-tab-button.spec.d.ts +1 -0
  14. package/lib/types/adw-tab-overview.spec.d.ts +1 -0
  15. package/lib/types/adw-view-switcher-sidebar.spec.d.ts +1 -0
  16. package/lib/types/adw-window.spec.d.ts +1 -0
  17. package/lib/types/attributes.d.ts +19 -0
  18. package/lib/types/breakpoints.d.ts +14 -3
  19. package/lib/types/construct-vectors.spec.d.ts +1 -0
  20. package/lib/types/elements/adw-action-row.d.ts +4 -0
  21. package/lib/types/elements/adw-application-window.d.ts +27 -0
  22. package/lib/types/elements/adw-bin.d.ts +17 -0
  23. package/lib/types/elements/adw-breakpoint-bin.d.ts +67 -0
  24. package/lib/types/elements/adw-clamp-scrollable.d.ts +38 -0
  25. package/lib/types/elements/adw-combo-row.d.ts +4 -0
  26. package/lib/types/elements/adw-dialog.d.ts +10 -0
  27. package/lib/types/elements/adw-entry-row.d.ts +6 -0
  28. package/lib/types/elements/adw-expander-row.d.ts +2 -0
  29. package/lib/types/elements/adw-header-bar.d.ts +6 -0
  30. package/lib/types/elements/adw-layout-slot.d.ts +19 -0
  31. package/lib/types/elements/adw-multi-layout-view.d.ts +101 -0
  32. package/lib/types/elements/adw-overlay-split-view.d.ts +13 -0
  33. package/lib/types/elements/adw-preferences-row.d.ts +31 -0
  34. package/lib/types/elements/adw-shortcuts-dialog.d.ts +13 -0
  35. package/lib/types/elements/adw-shortcuts-item.d.ts +36 -0
  36. package/lib/types/elements/adw-shortcuts-section.d.ts +14 -0
  37. package/lib/types/elements/adw-sidebar.d.ts +8 -2
  38. package/lib/types/elements/adw-spin-row.d.ts +6 -0
  39. package/lib/types/elements/adw-switch-row.d.ts +7 -0
  40. package/lib/types/elements/adw-tab-bar.d.ts +108 -0
  41. package/lib/types/elements/adw-tab-button.d.ts +32 -0
  42. package/lib/types/elements/adw-tab-overview.d.ts +199 -0
  43. package/lib/types/elements/adw-tab-view.d.ts +19 -23
  44. package/lib/types/elements/adw-toggle-group.d.ts +17 -0
  45. package/lib/types/elements/adw-view-switcher-sidebar.d.ts +84 -0
  46. package/lib/types/elements/adw-window.d.ts +40 -0
  47. package/lib/types/elements/gtk-about-dialog.d.ts +146 -0
  48. package/lib/types/elements/gtk-application-window.d.ts +34 -0
  49. package/lib/types/elements/gtk-aspect-frame.d.ts +27 -0
  50. package/lib/types/elements/gtk-button.d.ts +9 -0
  51. package/lib/types/elements/gtk-calendar.d.ts +159 -0
  52. package/lib/types/elements/gtk-center-box.d.ts +39 -0
  53. package/lib/types/elements/gtk-color-dialog-button.d.ts +69 -0
  54. package/lib/types/elements/gtk-column-view.d.ts +50 -0
  55. package/lib/types/elements/gtk-drag-icon.d.ts +78 -0
  56. package/lib/types/elements/gtk-drawing-area.d.ts +95 -0
  57. package/lib/types/elements/gtk-editable-label.d.ts +33 -0
  58. package/lib/types/elements/gtk-emoji-chooser.d.ts +84 -0
  59. package/lib/types/elements/gtk-entry.d.ts +20 -1
  60. package/lib/types/elements/gtk-expander.d.ts +49 -0
  61. package/lib/types/elements/gtk-fixed.d.ts +13 -0
  62. package/lib/types/elements/gtk-flow-box-child.d.ts +14 -0
  63. package/lib/types/elements/gtk-flow-box.d.ts +87 -0
  64. package/lib/types/elements/gtk-font-dialog-button.d.ts +83 -0
  65. package/lib/types/elements/gtk-frame.d.ts +24 -0
  66. package/lib/types/elements/gtk-gl-area.d.ts +136 -0
  67. package/lib/types/elements/gtk-graphics-offload.d.ts +24 -0
  68. package/lib/types/elements/gtk-grid-view.d.ts +39 -0
  69. package/lib/types/elements/gtk-grid.d.ts +27 -0
  70. package/lib/types/elements/gtk-header-bar.d.ts +60 -0
  71. package/lib/types/elements/gtk-inscription.d.ts +64 -0
  72. package/lib/types/elements/gtk-label.d.ts +3 -0
  73. package/lib/types/elements/gtk-level-bar.d.ts +59 -0
  74. package/lib/types/elements/gtk-link-button.d.ts +29 -0
  75. package/lib/types/elements/gtk-list-box-row.d.ts +54 -0
  76. package/lib/types/elements/gtk-list-box.d.ts +100 -0
  77. package/lib/types/elements/gtk-list-view.d.ts +73 -0
  78. package/lib/types/elements/gtk-media-controls.d.ts +142 -0
  79. package/lib/types/elements/gtk-notebook.d.ts +148 -0
  80. package/lib/types/elements/gtk-overlay.d.ts +22 -0
  81. package/lib/types/elements/gtk-page-setup-unix-dialog.d.ts +87 -0
  82. package/lib/types/elements/gtk-paned.d.ts +121 -0
  83. package/lib/types/elements/gtk-password-entry.d.ts +27 -0
  84. package/lib/types/elements/gtk-picture.d.ts +66 -0
  85. package/lib/types/elements/gtk-popover-bin.d.ts +82 -0
  86. package/lib/types/elements/gtk-popover-menu-bar.d.ts +66 -0
  87. package/lib/types/elements/gtk-popover-menu.d.ts +83 -0
  88. package/lib/types/elements/gtk-popover.d.ts +28 -3
  89. package/lib/types/elements/gtk-print-unix-dialog.d.ts +167 -0
  90. package/lib/types/elements/gtk-revealer.d.ts +55 -0
  91. package/lib/types/elements/gtk-scale-button.d.ts +59 -0
  92. package/lib/types/elements/gtk-scale.d.ts +98 -0
  93. package/lib/types/elements/gtk-scrollbar.d.ts +124 -0
  94. package/lib/types/elements/gtk-scrolled-window.d.ts +129 -0
  95. package/lib/types/elements/gtk-search-bar.d.ts +39 -0
  96. package/lib/types/elements/gtk-search-entry.d.ts +27 -0
  97. package/lib/types/elements/gtk-separator.d.ts +10 -0
  98. package/lib/types/elements/gtk-spin-button.d.ts +94 -0
  99. package/lib/types/elements/gtk-spinner.d.ts +14 -0
  100. package/lib/types/elements/gtk-stack-sidebar.d.ts +43 -0
  101. package/lib/types/elements/gtk-stack-switcher.d.ts +58 -0
  102. package/lib/types/elements/gtk-stack.d.ts +154 -0
  103. package/lib/types/elements/gtk-switch.d.ts +4 -1
  104. package/lib/types/elements/gtk-text-view.d.ts +62 -0
  105. package/lib/types/elements/gtk-text.d.ts +45 -0
  106. package/lib/types/elements/gtk-toggle-button.d.ts +45 -0
  107. package/lib/types/elements/gtk-tree-expander.d.ts +38 -0
  108. package/lib/types/elements/gtk-video.d.ts +97 -0
  109. package/lib/types/elements/gtk-viewport.d.ts +84 -0
  110. package/lib/types/elements/gtk-window-controls.d.ts +80 -0
  111. package/lib/types/elements/gtk-window-handle.d.ts +39 -0
  112. package/lib/types/elements/gtk-window.d.ts +103 -0
  113. package/lib/types/elements/justification.d.ts +2 -0
  114. package/lib/types/elements/popover-menu.d.ts +18 -0
  115. package/lib/types/elements/roving-focus.d.ts +29 -1
  116. package/lib/types/emoji-data.d.ts +38 -0
  117. package/lib/types/font-description.d.ts +96 -0
  118. package/lib/types/gtk-about-dialog.spec.d.ts +1 -0
  119. package/lib/types/gtk-action-bar.spec.d.ts +1 -0
  120. package/lib/types/gtk-application-window.spec.d.ts +1 -0
  121. package/lib/types/gtk-aspect-frame.spec.d.ts +1 -0
  122. package/lib/types/gtk-calendar.spec.d.ts +1 -0
  123. package/lib/types/gtk-center-box.spec.d.ts +1 -0
  124. package/lib/types/gtk-color-dialog-button.spec.d.ts +1 -0
  125. package/lib/types/gtk-column-view.spec.d.ts +1 -0
  126. package/lib/types/gtk-drag-icon.spec.d.ts +1 -0
  127. package/lib/types/gtk-drawing-area.spec.d.ts +1 -0
  128. package/lib/types/gtk-editable-label.spec.d.ts +1 -0
  129. package/lib/types/gtk-emoji-chooser.spec.d.ts +1 -0
  130. package/lib/types/gtk-expander.spec.d.ts +1 -0
  131. package/lib/types/gtk-fixed.spec.d.ts +1 -0
  132. package/lib/types/gtk-flow-box.spec.d.ts +1 -0
  133. package/lib/types/gtk-font-dialog-button.spec.d.ts +1 -0
  134. package/lib/types/gtk-frame.spec.d.ts +1 -0
  135. package/lib/types/gtk-gl-area.spec.d.ts +1 -0
  136. package/lib/types/gtk-graphics-offload.spec.d.ts +1 -0
  137. package/lib/types/gtk-grid-view.spec.d.ts +1 -0
  138. package/lib/types/gtk-grid.spec.d.ts +1 -0
  139. package/lib/types/gtk-header-bar.spec.d.ts +1 -0
  140. package/lib/types/gtk-inscription.spec.d.ts +1 -0
  141. package/lib/types/gtk-level-bar.spec.d.ts +1 -0
  142. package/lib/types/gtk-link-button.spec.d.ts +1 -0
  143. package/lib/types/gtk-list-box.spec.d.ts +1 -0
  144. package/lib/types/gtk-list-view.spec.d.ts +1 -0
  145. package/lib/types/gtk-media-controls.spec.d.ts +1 -0
  146. package/lib/types/gtk-notebook.spec.d.ts +1 -0
  147. package/lib/types/gtk-overlay.spec.d.ts +1 -0
  148. package/lib/types/gtk-paned.spec.d.ts +1 -0
  149. package/lib/types/gtk-password-entry.spec.d.ts +1 -0
  150. package/lib/types/gtk-picture.spec.d.ts +1 -0
  151. package/lib/types/gtk-popover-bin.spec.d.ts +1 -0
  152. package/lib/types/gtk-popover-menu-bar.spec.d.ts +1 -0
  153. package/lib/types/gtk-popover-menu.spec.d.ts +1 -0
  154. package/lib/types/gtk-popover-role.spec.d.ts +1 -0
  155. package/lib/types/gtk-popover.spec.d.ts +1 -0
  156. package/lib/types/gtk-print-dialogs.spec.d.ts +1 -0
  157. package/lib/types/gtk-revealer.spec.d.ts +1 -0
  158. package/lib/types/gtk-scale-button.spec.d.ts +1 -0
  159. package/lib/types/gtk-scale.spec.d.ts +1 -0
  160. package/lib/types/gtk-scrollbar.spec.d.ts +1 -0
  161. package/lib/types/gtk-scrolled-window.spec.d.ts +1 -0
  162. package/lib/types/gtk-search-bar.spec.d.ts +1 -0
  163. package/lib/types/gtk-search-entry.spec.d.ts +1 -0
  164. package/lib/types/gtk-separator.spec.d.ts +1 -0
  165. package/lib/types/gtk-spin-button.spec.d.ts +1 -0
  166. package/lib/types/gtk-spinner.spec.d.ts +1 -0
  167. package/lib/types/gtk-stack-sidebar.spec.d.ts +1 -0
  168. package/lib/types/gtk-stack-switcher.spec.d.ts +1 -0
  169. package/lib/types/gtk-stack.spec.d.ts +1 -0
  170. package/lib/types/gtk-text-view.spec.d.ts +1 -0
  171. package/lib/types/gtk-text.spec.d.ts +1 -0
  172. package/lib/types/gtk-toggle-button.spec.d.ts +1 -0
  173. package/lib/types/gtk-tree-expander.spec.d.ts +1 -0
  174. package/lib/types/gtk-video.spec.d.ts +1 -0
  175. package/lib/types/gtk-viewport.spec.d.ts +1 -0
  176. package/lib/types/gtk-window-controls.spec.d.ts +1 -0
  177. package/lib/types/gtk-window-handle.spec.d.ts +1 -0
  178. package/lib/types/gtk-window.spec.d.ts +1 -0
  179. package/lib/types/index.d.ts +4 -1
  180. package/lib/types/list-view-dom.d.ts +44 -0
  181. package/lib/types/namespace/adw.d.ts +12 -0
  182. package/lib/types/namespace/gtk.d.ts +61 -0
  183. package/lib/types/page-setup.d.ts +100 -0
  184. package/lib/types/row-line-clamp.d.ts +20 -0
  185. package/lib/types/row-tooltip.d.ts +8 -0
  186. package/lib/types/row-tooltip.spec.d.ts +1 -0
  187. package/lib/types/scroll-shading.d.ts +15 -1
  188. package/lib/types/shared-tree-builder.d.ts +75 -17
  189. package/lib/types/shared-tree-mount.spec.d.ts +1 -0
  190. package/lib/types/signals.d.ts +4 -0
  191. package/lib/types/styles.generated.d.ts +1 -1
  192. package/lib/types/tab-chip.d.ts +73 -0
  193. package/lib/types/template-classes.d.ts +11 -0
  194. package/lib/types/widget-size.spec.d.ts +1 -0
  195. package/package.json +12 -8
  196. package/scss/_action_row.scss +41 -17
  197. package/scss/_application_window.scss +26 -0
  198. package/scss/_aspect_frame.scss +25 -0
  199. package/scss/_bin.scss +26 -0
  200. package/scss/_button.scss +45 -0
  201. package/scss/_button_row.scss +5 -0
  202. package/scss/_calendar.scss +176 -0
  203. package/scss/_center_box.scss +25 -0
  204. package/scss/_checks.scss +6 -1
  205. package/scss/_clamp_scrollable.scss +28 -0
  206. package/scss/_color_button.scss +67 -0
  207. package/scss/_column_view.scss +126 -0
  208. package/scss/_drag_icon.scss +55 -0
  209. package/scss/_drawing.scss +85 -0
  210. package/scss/_emoji_chooser.scss +213 -0
  211. package/scss/_entry.scss +13 -5
  212. package/scss/_entry_row.scss +1 -1
  213. package/scss/_expander.scss +113 -0
  214. package/scss/_expander_row.scss +36 -3
  215. package/scss/_fixed.scss +29 -0
  216. package/scss/_flow_box.scss +94 -0
  217. package/scss/_font_button.scss +63 -0
  218. package/scss/_frame.scss +41 -0
  219. package/scss/_grid.scss +19 -0
  220. package/scss/_grid_view.scss +46 -0
  221. package/scss/_gtk_about_dialog.scss +186 -0
  222. package/scss/_headerbar.scss +134 -1
  223. package/scss/_icons.generated.scss +186 -0
  224. package/scss/_inscription.scss +125 -0
  225. package/scss/_level_bar.scss +190 -0
  226. package/scss/_link.scss +82 -0
  227. package/scss/_list_box.scss +88 -0
  228. package/scss/_list_view.scss +96 -0
  229. package/scss/_media_controls.scss +139 -0
  230. package/scss/_overlay.scss +68 -0
  231. package/scss/_paned.scss +130 -0
  232. package/scss/_password_entry.scss +83 -0
  233. package/scss/_picture.scss +80 -0
  234. package/scss/_popover_bin.scss +53 -0
  235. package/scss/_popover_menu_bar.scss +104 -0
  236. package/scss/_preferences_row.scss +60 -0
  237. package/scss/_print_dialogs.scss +328 -0
  238. package/scss/_reset.scss +21 -19
  239. package/scss/_revealer.scss +37 -0
  240. package/scss/_row.scss +36 -5
  241. package/scss/_scale.scss +298 -0
  242. package/scss/_scale_button.scss +167 -0
  243. package/scss/_scrollbar.scss +230 -0
  244. package/scss/_scrolled_window.scss +131 -0
  245. package/scss/_search_bar.scss +118 -0
  246. package/scss/_search_entry.scss +86 -0
  247. package/scss/_separator.scss +44 -0
  248. package/scss/_shortcuts_dialog.scss +100 -0
  249. package/scss/_spin_button.scss +172 -0
  250. package/scss/_spin_row.scss +56 -8
  251. package/scss/_spinner.scss +85 -4
  252. package/scss/_stack.scss +729 -0
  253. package/scss/_status_page.scss +1 -1
  254. package/scss/_switch.scss +13 -5
  255. package/scss/_switch_row.scss +10 -0
  256. package/scss/_tab_bar.scss +317 -0
  257. package/scss/_tab_button.scss +98 -0
  258. package/scss/_tab_overview.scss +483 -0
  259. package/scss/_text.scss +182 -0
  260. package/scss/_theme.scss +1 -1
  261. package/scss/_toggle_group.scss +28 -0
  262. package/scss/_tree_expander.scss +62 -0
  263. package/scss/_variables.scss +1 -1
  264. package/scss/_video.scss +146 -0
  265. package/scss/_view_switcher_sidebar.scss +112 -0
  266. package/scss/_viewport.scss +50 -0
  267. package/scss/_widget.scss +14 -3
  268. package/scss/_window.scss +131 -2
  269. package/scss/_window_handle.scss +20 -0
  270. package/scss/adwaita-skin.scss +109 -0
  271. package/src/adw-action-rows.spec.ts +331 -0
  272. package/src/adw-application-window.spec.ts +118 -0
  273. package/src/adw-bin.spec.ts +75 -0
  274. package/src/adw-breakpoint-bin.spec.ts +324 -0
  275. package/src/adw-clamp-scrollable.spec.ts +189 -0
  276. package/src/adw-header-bar.spec.ts +18 -0
  277. package/src/adw-multi-layout-view.spec.ts +243 -0
  278. package/src/adw-preferences-row.spec.ts +164 -0
  279. package/src/adw-row-state.spec.ts +142 -0
  280. package/src/adw-shortcuts.spec.ts +155 -0
  281. package/src/adw-status-page.spec.ts +124 -0
  282. package/src/adw-tab-bar.spec.ts +287 -0
  283. package/src/adw-tab-button.spec.ts +101 -0
  284. package/src/adw-tab-overview.spec.ts +443 -0
  285. package/src/adw-view-switcher-sidebar.spec.ts +281 -0
  286. package/src/adw-window.spec.ts +129 -0
  287. package/src/attributes.ts +52 -0
  288. package/src/breakpoints.ts +25 -8
  289. package/src/capabilities.d.mts +3 -0
  290. package/src/capabilities.mjs +21 -0
  291. package/src/connect-lifecycle.spec.ts +12 -0
  292. package/src/construct-vectors.spec.ts +175 -0
  293. package/src/elements/adw-action-row.ts +31 -2
  294. package/src/elements/adw-alert-dialog.ts +4 -1
  295. package/src/elements/adw-application-window.ts +154 -0
  296. package/src/elements/adw-bin.ts +101 -0
  297. package/src/elements/adw-breakpoint-bin.ts +287 -0
  298. package/src/elements/adw-button-row.ts +7 -1
  299. package/src/elements/adw-clamp-scrollable.ts +207 -0
  300. package/src/elements/adw-combo-row.ts +26 -1
  301. package/src/elements/adw-dialog.ts +19 -0
  302. package/src/elements/adw-entry-row.ts +22 -12
  303. package/src/elements/adw-expander-row.ts +34 -1
  304. package/src/elements/adw-header-bar.ts +18 -4
  305. package/src/elements/adw-layout-slot.ts +95 -0
  306. package/src/elements/adw-multi-layout-view.ts +326 -0
  307. package/src/elements/adw-overlay-split-view.ts +29 -6
  308. package/src/elements/adw-preferences-row.ts +153 -0
  309. package/src/elements/adw-shortcuts-dialog.ts +52 -0
  310. package/src/elements/adw-shortcuts-item.ts +171 -0
  311. package/src/elements/adw-shortcuts-section.ts +86 -0
  312. package/src/elements/adw-sidebar.ts +74 -7
  313. package/src/elements/adw-spin-row.ts +37 -2
  314. package/src/elements/adw-status-page.ts +10 -2
  315. package/src/elements/adw-switch-row.ts +53 -2
  316. package/src/elements/adw-tab-bar.ts +529 -0
  317. package/src/elements/adw-tab-button.ts +194 -0
  318. package/src/elements/adw-tab-overview.ts +1011 -0
  319. package/src/elements/adw-tab-view.ts +66 -145
  320. package/src/elements/adw-toggle-group.ts +138 -14
  321. package/src/elements/adw-view-stack.ts +18 -0
  322. package/src/elements/adw-view-switcher-sidebar.ts +332 -0
  323. package/src/elements/adw-window.ts +190 -11
  324. package/src/elements/checks.ts +35 -8
  325. package/src/elements/gtk-about-dialog.ts +811 -0
  326. package/src/elements/gtk-application-window.ts +108 -0
  327. package/src/elements/gtk-aspect-frame.ts +160 -0
  328. package/src/elements/gtk-button.ts +12 -0
  329. package/src/elements/gtk-calendar.ts +757 -0
  330. package/src/elements/gtk-center-box.ts +184 -0
  331. package/src/elements/gtk-color-dialog-button.ts +278 -0
  332. package/src/elements/gtk-column-view.ts +320 -0
  333. package/src/elements/gtk-drag-icon.ts +257 -0
  334. package/src/elements/gtk-drawing-area.ts +303 -0
  335. package/src/elements/gtk-editable-label.ts +204 -0
  336. package/src/elements/gtk-emoji-chooser.ts +542 -0
  337. package/src/elements/gtk-entry.ts +39 -5
  338. package/src/elements/gtk-expander.ts +301 -0
  339. package/src/elements/gtk-fixed.ts +99 -0
  340. package/src/elements/gtk-flow-box-child.ts +68 -0
  341. package/src/elements/gtk-flow-box.ts +480 -0
  342. package/src/elements/gtk-font-dialog-button.ts +432 -0
  343. package/src/elements/gtk-frame.ts +174 -0
  344. package/src/elements/gtk-gl-area.ts +476 -0
  345. package/src/elements/gtk-graphics-offload.ts +130 -0
  346. package/src/elements/gtk-grid-view.ts +168 -0
  347. package/src/elements/gtk-grid.ts +127 -0
  348. package/src/elements/gtk-header-bar.ts +273 -0
  349. package/src/elements/gtk-inscription.ts +387 -0
  350. package/src/elements/gtk-label.ts +19 -11
  351. package/src/elements/gtk-level-bar.ts +323 -0
  352. package/src/elements/gtk-link-button.ts +138 -0
  353. package/src/elements/gtk-list-box-row.ts +195 -0
  354. package/src/elements/gtk-list-box.ts +413 -0
  355. package/src/elements/gtk-list-view.ts +370 -0
  356. package/src/elements/gtk-media-controls.ts +509 -0
  357. package/src/elements/gtk-notebook.ts +664 -0
  358. package/src/elements/gtk-overlay.ts +149 -0
  359. package/src/elements/gtk-page-setup-unix-dialog.ts +420 -0
  360. package/src/elements/gtk-paned.ts +597 -0
  361. package/src/elements/gtk-password-entry.ts +170 -0
  362. package/src/elements/gtk-picture.ts +254 -0
  363. package/src/elements/gtk-popover-bin.ts +332 -0
  364. package/src/elements/gtk-popover-menu-bar.ts +355 -0
  365. package/src/elements/gtk-popover-menu.ts +339 -0
  366. package/src/elements/gtk-popover.ts +92 -10
  367. package/src/elements/gtk-print-unix-dialog.ts +989 -0
  368. package/src/elements/gtk-progress-bar.ts +24 -11
  369. package/src/elements/gtk-revealer.ts +406 -0
  370. package/src/elements/gtk-scale-button.ts +358 -0
  371. package/src/elements/gtk-scale.ts +492 -0
  372. package/src/elements/gtk-scrollbar.ts +528 -0
  373. package/src/elements/gtk-scrolled-window.ts +653 -0
  374. package/src/elements/gtk-search-bar.ts +200 -0
  375. package/src/elements/gtk-search-entry.ts +172 -0
  376. package/src/elements/gtk-separator.ts +52 -0
  377. package/src/elements/gtk-spin-button.ts +475 -0
  378. package/src/elements/gtk-spinner.ts +126 -0
  379. package/src/elements/gtk-stack-sidebar.ts +212 -0
  380. package/src/elements/gtk-stack-switcher.ts +279 -0
  381. package/src/elements/gtk-stack.ts +748 -0
  382. package/src/elements/gtk-switch.ts +52 -19
  383. package/src/elements/gtk-text-view.ts +290 -0
  384. package/src/elements/gtk-text.ts +219 -0
  385. package/src/elements/gtk-toggle-button.ts +239 -0
  386. package/src/elements/gtk-tree-expander.ts +243 -0
  387. package/src/elements/gtk-video.ts +423 -0
  388. package/src/elements/gtk-viewport.ts +277 -0
  389. package/src/elements/gtk-window-controls.ts +341 -0
  390. package/src/elements/gtk-window-handle.ts +221 -0
  391. package/src/elements/gtk-window.ts +372 -0
  392. package/src/elements/justification.ts +24 -0
  393. package/src/elements/popover-menu.ts +24 -0
  394. package/src/elements/roving-focus.ts +53 -21
  395. package/src/emoji-data.ts +577 -0
  396. package/src/entry-rows.spec.ts +31 -0
  397. package/src/font-description.ts +238 -0
  398. package/src/gtk-about-dialog.spec.ts +250 -0
  399. package/src/gtk-action-bar.spec.ts +89 -0
  400. package/src/gtk-application-window.spec.ts +107 -0
  401. package/src/gtk-aspect-frame.spec.ts +114 -0
  402. package/src/gtk-calendar.spec.ts +589 -0
  403. package/src/gtk-center-box.spec.ts +125 -0
  404. package/src/gtk-color-dialog-button.spec.ts +141 -0
  405. package/src/gtk-column-view.spec.ts +154 -0
  406. package/src/gtk-drag-icon.spec.ts +190 -0
  407. package/src/gtk-drawing-area.spec.ts +211 -0
  408. package/src/gtk-editable-label.spec.ts +190 -0
  409. package/src/gtk-emoji-chooser.spec.ts +173 -0
  410. package/src/gtk-entry.spec.ts +23 -0
  411. package/src/gtk-expander.spec.ts +170 -0
  412. package/src/gtk-fixed.spec.ts +97 -0
  413. package/src/gtk-flow-box.spec.ts +289 -0
  414. package/src/gtk-font-dialog-button.spec.ts +166 -0
  415. package/src/gtk-frame.spec.ts +119 -0
  416. package/src/gtk-gl-area.spec.ts +361 -0
  417. package/src/gtk-graphics-offload.spec.ts +119 -0
  418. package/src/gtk-grid-view.spec.ts +106 -0
  419. package/src/gtk-grid.spec.ts +122 -0
  420. package/src/gtk-header-bar.spec.ts +202 -0
  421. package/src/gtk-inscription.spec.ts +235 -0
  422. package/src/gtk-level-bar.spec.ts +199 -0
  423. package/src/gtk-link-button.spec.ts +117 -0
  424. package/src/gtk-list-box.spec.ts +422 -0
  425. package/src/gtk-list-view.spec.ts +203 -0
  426. package/src/gtk-media-controls.spec.ts +295 -0
  427. package/src/gtk-notebook.spec.ts +546 -0
  428. package/src/gtk-overlay.spec.ts +141 -0
  429. package/src/gtk-paned.spec.ts +267 -0
  430. package/src/gtk-password-entry.spec.ts +150 -0
  431. package/src/gtk-picture.spec.ts +172 -0
  432. package/src/gtk-popover-bin.spec.ts +229 -0
  433. package/src/gtk-popover-menu-bar.spec.ts +296 -0
  434. package/src/gtk-popover-menu.spec.ts +261 -0
  435. package/src/gtk-popover-role.spec.ts +138 -0
  436. package/src/gtk-popover.spec.ts +264 -0
  437. package/src/gtk-print-dialogs.spec.ts +251 -0
  438. package/src/gtk-revealer.spec.ts +164 -0
  439. package/src/gtk-scale-button.spec.ts +218 -0
  440. package/src/gtk-scale.spec.ts +213 -0
  441. package/src/gtk-scrollbar.spec.ts +244 -0
  442. package/src/gtk-scrolled-window.spec.ts +262 -0
  443. package/src/gtk-search-bar.spec.ts +179 -0
  444. package/src/gtk-search-entry.spec.ts +142 -0
  445. package/src/gtk-separator.spec.ts +74 -0
  446. package/src/gtk-spin-button.spec.ts +186 -0
  447. package/src/gtk-spinner.spec.ts +130 -0
  448. package/src/gtk-stack-sidebar.spec.ts +217 -0
  449. package/src/gtk-stack-switcher.spec.ts +249 -0
  450. package/src/gtk-stack.spec.ts +432 -0
  451. package/src/gtk-text-view.spec.ts +189 -0
  452. package/src/gtk-text.spec.ts +163 -0
  453. package/src/gtk-toggle-button.spec.ts +186 -0
  454. package/src/gtk-tree-expander.spec.ts +201 -0
  455. package/src/gtk-video.spec.ts +240 -0
  456. package/src/gtk-viewport.spec.ts +140 -0
  457. package/src/gtk-window-controls.spec.ts +232 -0
  458. package/src/gtk-window-handle.spec.ts +158 -0
  459. package/src/gtk-window.spec.ts +202 -0
  460. package/src/index.ts +9 -1
  461. package/src/keyboard-operable.spec.ts +77 -9
  462. package/src/list-view-dom.ts +121 -0
  463. package/src/namespace/adw.ts +12 -0
  464. package/src/namespace/gtk.ts +61 -0
  465. package/src/page-setup.ts +222 -0
  466. package/src/row-line-clamp.ts +74 -0
  467. package/src/row-tooltip.spec.ts +313 -0
  468. package/src/row-tooltip.ts +52 -0
  469. package/src/scroll-shading.ts +18 -1
  470. package/src/shared-tree-builder.ts +336 -41
  471. package/src/shared-tree-mount.spec.ts +130 -0
  472. package/src/shared-trees.spec.ts +15 -0
  473. package/src/signals.ts +15 -0
  474. package/src/style-classes.spec.ts +9 -8
  475. package/src/styles.generated.ts +1 -1
  476. package/src/tab-chip.ts +200 -0
  477. package/src/template-classes.ts +45 -0
  478. package/src/test.browser.mts +160 -0
  479. package/src/widget-size.spec.ts +117 -0
@@ -0,0 +1,211 @@
1
+ // DOM-level tests for <gtk-drawing-area>. Three separable things get their own describe:
2
+ // the SIZE (the content properties and the floor they are), the CALLBACK (what it is
3
+ // handed and how often it runs), and the NOTIFICATION pair — `resize` for the allocation
4
+ // and `notify::` for the properties, which fire on different things in the C.
5
+ import { describe, expect, it } from '@gjsify/unit';
6
+
7
+ import type { GtkDrawingArea } from './elements/gtk-drawing-area.js';
8
+
9
+ function mount(attrs: Record<string, string> = {}): { el: GtkDrawingArea; host: HTMLElement } {
10
+ const host = document.createElement('div');
11
+ document.body.appendChild(host);
12
+ const el = document.createElement('gtk-drawing-area') as GtkDrawingArea;
13
+ for (const [name, value] of Object.entries(attrs)) el.setAttribute(name, value);
14
+ host.appendChild(el);
15
+ return { el, host };
16
+ }
17
+
18
+ /**
19
+ * Two frames, not one: the platform delivers a ResizeObserver's first observation AFTER
20
+ * the animation-frame callbacks of the frame it belongs to, so a single `rAF` resolves
21
+ * before the allocation the first draw needs has been recorded.
22
+ */
23
+ function settled(): Promise<void> {
24
+ return new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(() => resolve())));
25
+ }
26
+
27
+ export const GtkDrawingAreaTest = async () => {
28
+ await describe('<gtk-drawing-area> size', async () => {
29
+ await it('defaults to no intrinsic size on either axis', () => {
30
+ // gtkdrawingarea.c:288-304 — both pspecs default to 0.
31
+ const { el, host } = mount();
32
+ expect(el.contentWidth).toBe(0);
33
+ expect(el.contentHeight).toBe(0);
34
+ host.remove();
35
+ });
36
+
37
+ await it('reads the two GIR properties as attributes', () => {
38
+ const { el, host } = mount({ 'content-width': '160', 'content-height': '120' });
39
+ expect(el.contentWidth).toBe(160);
40
+ expect(el.contentHeight).toBe(120);
41
+ host.remove();
42
+ });
43
+
44
+ await it('clamps a write outside the pspec range instead of refusing it', () => {
45
+ // g_param_spec_int ("content-width", …, 0, G_MAXINT, 0, …) at :290-294.
46
+ const { el, host } = mount();
47
+ el.contentWidth = 999;
48
+ expect(el.contentWidth).toBe(999);
49
+ el.contentWidth = -50;
50
+ expect(el.contentWidth).toBe(0);
51
+ el.contentHeight = 1e12;
52
+ expect(el.contentHeight).toBe(2147483647);
53
+ host.remove();
54
+ });
55
+
56
+ await it('notifies only when the value really moves', () => {
57
+ // gtkdrawingarea.c:373 — `if (priv->content_width == width) return;`.
58
+ const { el, host } = mount({ 'content-width': '160' });
59
+ const seen: unknown[] = [];
60
+ el.addEventListener('notify::content-width', (event) =>
61
+ seen.push((event as CustomEvent).detail['content-width']),
62
+ );
63
+ el.contentWidth = 160;
64
+ expect(seen).toStrictEqual([]);
65
+ el.contentWidth = 200;
66
+ expect(seen).toStrictEqual([200]);
67
+ host.remove();
68
+ });
69
+
70
+ await it('is the minimum AND the natural size, so the floor follows the number', () => {
71
+ // gtkdrawingarea.c:210-231 — `*minimum = *natural = priv->content_width`, which
72
+ // `_drawing.scss` spells as one custom property read by `width` AND `min-width`.
73
+ const { el, host } = mount({ 'content-width': '160', 'content-height': '120' });
74
+ expect(el.style.getPropertyValue('--adw-drawing-width')).toBe('160px');
75
+ expect(el.style.getPropertyValue('--adw-drawing-height')).toBe('120px');
76
+ expect(el.querySelector('canvas')).not.toBe(null);
77
+ // At the zero default there is no intrinsic size at all, so the property is
78
+ // GONE and the stylesheet's `auto` fallback is what answers.
79
+ el.contentWidth = 0;
80
+ expect(el.style.getPropertyValue('--adw-drawing-width')).toBe('');
81
+ host.remove();
82
+ });
83
+ });
84
+
85
+ await describe('<gtk-drawing-area> the draw function', async () => {
86
+ await it('has none until one is set, and paints nothing', () => {
87
+ // gtkdrawingarea.c:251-252 — `if (!priv->draw_func) return;`.
88
+ const { el, host } = mount({ 'content-width': '40', 'content-height': '40' });
89
+ expect(el.drawFunc).toBe(null);
90
+ host.remove();
91
+ });
92
+
93
+ await it('is called with the ALLOCATION, not with the content size', async () => {
94
+ const { el, host } = mount({ 'content-width': '40', 'content-height': '40' });
95
+ const calls: number[][] = [];
96
+ el.setDrawFunc((_area, cr, width, height) => {
97
+ calls.push([width, height, Math.round(cr.lineWidth)]);
98
+ cr.fillStyle = '#ff0000';
99
+ cr.fillRect(0, 0, width, height);
100
+ });
101
+ await settled();
102
+ expect(calls.length).toBeGreaterThan(0);
103
+ // The callback runs, and the numbers it gets are the box the element was
104
+ // allocated — never the two properties (:248-262).
105
+ expect(calls[0][0]).toBeGreaterThanOrEqual(40);
106
+ expect(calls[0][1]).toBeGreaterThanOrEqual(40);
107
+ const canvas = el.querySelector('canvas') as HTMLCanvasElement;
108
+ expect(canvas.width).toBeGreaterThan(0);
109
+ host.remove();
110
+ });
111
+
112
+ await it('runs again when queue_draw asks for it', async () => {
113
+ const { el, host } = mount({ 'content-width': '40', 'content-height': '40' });
114
+ let calls = 0;
115
+ el.setDrawFunc(() => {
116
+ calls += 1;
117
+ });
118
+ await settled();
119
+ const seen = calls;
120
+ // gtkdrawingarea.c:127-129 — queue_draw calls the draw function again, on the
121
+ // NEXT FRAME: the C marks the window dirty and the frame clock draws it.
122
+ el.queueDraw();
123
+ expect(calls).toBe(seen);
124
+ await settled();
125
+ expect(calls).toBeGreaterThan(seen);
126
+ host.remove();
127
+ });
128
+
129
+ await it('installing one repaints with it, and clearing it blanks the widget', async () => {
130
+ const { el, host } = mount({ 'content-width': '40', 'content-height': '40' });
131
+ let calls = 0;
132
+ el.setDrawFunc(() => {
133
+ calls += 1;
134
+ });
135
+ await settled();
136
+ const seen = calls;
137
+ expect(seen).toBeGreaterThan(0);
138
+ // `set_draw_func` ends in `gtk_widget_queue_draw` (gtkdrawingarea.c:489), and
139
+ // the snapshot that answers calls the NEW function — so the install itself is
140
+ // what repaints, not the next unrelated frame.
141
+ el.setDrawFunc(() => {
142
+ calls += 1;
143
+ });
144
+ await settled();
145
+ expect(calls).toBe(seen + 1);
146
+ // With no function the snapshot returns before drawing (:251-252), so nothing
147
+ // is painted from here on however often a redraw is asked for.
148
+ const blanked = calls;
149
+ el.setDrawFunc(null);
150
+ el.queueDraw();
151
+ await settled();
152
+ expect(el.drawFunc).toBe(null);
153
+ expect(calls).toBe(blanked);
154
+ host.remove();
155
+ });
156
+
157
+ await it('hands the callback a context that starts each frame empty', async () => {
158
+ // GTK's renderer gives every frame a fresh surface; a canvas keeps its pixels,
159
+ // so the element clears — the one place a naive port shows (see the header).
160
+ const { el, host } = mount({ 'content-width': '40', 'content-height': '40' });
161
+ let pixel: number[] = [];
162
+ el.setDrawFunc((_area, cr, _width, _height) => {
163
+ cr.fillStyle = '#0000ff';
164
+ cr.fillRect(0, 0, 1, 1);
165
+ pixel = [...cr.getImageData(0, 0, 1, 1).data];
166
+ });
167
+ await settled();
168
+ expect(pixel[2]).toBe(255);
169
+ el.queueDraw();
170
+ expect(pixel[2]).toBe(255);
171
+ host.remove();
172
+ });
173
+ });
174
+
175
+ await describe('<gtk-drawing-area> notifications', async () => {
176
+ await it('emits resize with the allocation, once per change', async () => {
177
+ // gtkdrawingarea.c:233-238 — size_allocate emits ::resize and does nothing else.
178
+ const { el, host } = mount({ 'content-width': '40', 'content-height': '40' });
179
+ const seen: { width: number; height: number }[] = [];
180
+ // `resize` is a UIEvent name in lib.dom, so the listener parameter arrives typed.
181
+ el.addEventListener('resize', (event) => seen.push((event as unknown as CustomEvent).detail));
182
+ await settled();
183
+ expect(seen.length).toBe(1);
184
+ expect(seen[0].width).toBeGreaterThanOrEqual(40);
185
+ el.style.width = '80px';
186
+ await settled();
187
+ expect(seen.length).toBe(2);
188
+ expect(seen[1].width).toBe(80);
189
+ host.remove();
190
+ });
191
+
192
+ await it('re-arms its observer when the element comes back', async () => {
193
+ // The ResizeObserver is released on the way out, so a re-parent has to arm it
194
+ // again — the shape `scripts/check-adwaita-connect-rebind.mjs` holds for.
195
+ const { el, host } = mount({ 'content-width': '40', 'content-height': '40' });
196
+ await settled();
197
+ const parked = document.createElement('div');
198
+ document.body.appendChild(parked);
199
+ host.remove();
200
+ parked.appendChild(el);
201
+ const seen: unknown[] = [];
202
+ // `resize` is a UIEvent name in lib.dom, so the listener parameter arrives typed.
203
+ el.addEventListener('resize', (event) => seen.push((event as unknown as CustomEvent).detail));
204
+ el.style.width = '96px';
205
+ await settled();
206
+ expect(seen.length).toBe(1);
207
+ parked.remove();
208
+ host.remove();
209
+ });
210
+ });
211
+ };
@@ -0,0 +1,190 @@
1
+ // DOM-level tests for `<gtk-editable-label>`: the stack's visible child, which is what
2
+ // `editing` IS (gtkeditablelabel.c:595-599), and the four ways editing ends — Enter commits,
3
+ // Escape discards, losing focus commits after the C's grace period, and `editable` FALSE
4
+ // stops without committing.
5
+ //
6
+ // The draft is the point of the widget: while the entry is visible it holds a text the label
7
+ // does not have yet, so every row here checks what the LABEL says afterwards and not only
8
+ // what the entry did.
9
+
10
+ import { describe, expect, it } from '@gjsify/unit';
11
+
12
+ import type { GtkEditableLabel } from './elements/gtk-editable-label.js';
13
+
14
+ function mount(attrs: Record<string, string> = {}): GtkEditableLabel {
15
+ const el = document.createElement('gtk-editable-label') as GtkEditableLabel;
16
+ for (const [name, value] of Object.entries(attrs)) el.setAttribute(name, value);
17
+ document.body.appendChild(el);
18
+ return el;
19
+ }
20
+
21
+ function unmountAll(): void {
22
+ for (const el of Array.from(document.querySelectorAll('gtk-editable-label'))) el.remove();
23
+ }
24
+
25
+ /** The label half of the stack — `querySelector` is `Element`-typed, so the view is named. */
26
+ function labelOf(el: GtkEditableLabel): HTMLElement {
27
+ return el.querySelector<HTMLElement>('.adw-editable-label-label') as HTMLElement;
28
+ }
29
+
30
+ const key = (target: HTMLElement, name: string): KeyboardEvent => {
31
+ const event = new KeyboardEvent('keydown', { key: name, bubbles: true, cancelable: true });
32
+ target.dispatchEvent(event);
33
+ return event;
34
+ };
35
+
36
+ const settle = (ms: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, ms));
37
+
38
+ export const GtkEditableLabelTest = async () => {
39
+ await describe('<gtk-editable-label> the stack', async () => {
40
+ await it('shows the label and not the entry until editing starts', () => {
41
+ const el = mount({ text: 'Ada Lovelace' });
42
+ expect(el.editing).toBe(false);
43
+ expect(el.classList.contains('editing')).toBe(false);
44
+ expect(labelOf(el).hidden).toBe(false);
45
+ expect(el.entry.hidden).toBe(true);
46
+ expect(el.getAttribute('tabindex')).toBe('0');
47
+ unmountAll();
48
+ });
49
+
50
+ await it('swaps the visible child when `editing` is set, and takes the tab stop', () => {
51
+ const el = mount({ text: 'Ada Lovelace', editing: '' });
52
+ expect(el.editing).toBe(true);
53
+ expect(el.classList.contains('editing')).toBe(true);
54
+ expect(labelOf(el).hidden).toBe(true);
55
+ expect(el.entry.hidden).toBe(false);
56
+ expect(el.hasAttribute('tabindex')).toBe(false);
57
+ unmountAll();
58
+ });
59
+
60
+ await it('notifies on every real change and on none that did not happen', () => {
61
+ const el = mount({ text: 'x' });
62
+ const seen: unknown[] = [];
63
+ el.addEventListener('notify::editing', (event) => seen.push((event as CustomEvent).detail));
64
+ el.editing = true;
65
+ el.editing = true;
66
+ el.editing = false;
67
+ expect(seen).toStrictEqual([{ editing: true }, { editing: false }]);
68
+ unmountAll();
69
+ });
70
+ });
71
+
72
+ await describe('<gtk-editable-label> starting', async () => {
73
+ await it('starts on a click of the LABEL, which is where the C puts the gesture', () => {
74
+ const el = mount({ text: 'x' });
75
+ labelOf(el).click();
76
+ expect(el.editing).toBe(true);
77
+ unmountAll();
78
+ });
79
+
80
+ // The keys are a binding of the WIDGET (gtkeditablelabel.c:54-63), so the event is
81
+ // dispatched on the host and not on the label node inside the stack.
82
+ await it('starts on Enter, and on Space where the C binds both', () => {
83
+ const label = mount({ text: 'x' });
84
+ key(label, 'Enter');
85
+ expect(label.editing).toBe(true);
86
+ label.editing = false;
87
+ key(label, ' ');
88
+ expect(label.editing).toBe(true);
89
+ unmountAll();
90
+ });
91
+
92
+ await it('refuses to start while `editable` is FALSE, as both actions are disabled', () => {
93
+ const el = mount({ text: 'x', editable: 'false' });
94
+ expect(el.editable).toBe(false);
95
+ labelOf(el).click();
96
+ expect(el.editing).toBe(false);
97
+ el.startEditing();
98
+ expect(el.editing).toBe(false);
99
+ unmountAll();
100
+ });
101
+ });
102
+
103
+ await describe('<gtk-editable-label> committing and discarding', async () => {
104
+ await it('Enter in the entry COMMITS: the draft becomes the label text', () => {
105
+ const el = mount({ text: 'Ada' });
106
+ el.startEditing();
107
+ el.entry.value = 'Ada Lovelace';
108
+ key(el.entry, 'Enter');
109
+ expect(el.editing).toBe(false);
110
+ expect(el.text).toBe('Ada Lovelace');
111
+ expect(el.querySelector('.adw-editable-label-label')?.textContent).toBe('Ada Lovelace');
112
+ unmountAll();
113
+ });
114
+
115
+ await it('Escape in the entry DISCARDS it: the label keeps what it had', () => {
116
+ const el = mount({ text: 'Ada' });
117
+ el.startEditing();
118
+ el.entry.value = 'Ada Lovelace';
119
+ key(el.entry, 'Escape');
120
+ expect(el.editing).toBe(false);
121
+ expect(el.text).toBe('Ada');
122
+ expect(el.querySelector('.adw-editable-label-label')?.textContent).toBe('Ada');
123
+ // …and the entry is reset, so the next edit starts from the committed text.
124
+ el.startEditing();
125
+ expect(el.entry.value).toBe('Ada');
126
+ unmountAll();
127
+ });
128
+
129
+ await it("losing focus COMMITS, after the C's grace period", async () => {
130
+ const el = mount({ text: 'Ada' });
131
+ el.startEditing();
132
+ el.entry.value = 'Ada Lovelace';
133
+ const outside = document.createElement('button');
134
+ document.body.appendChild(outside);
135
+ el.dispatchEvent(new FocusEvent('focusout', { bubbles: false, relatedTarget: outside }));
136
+ // Inside the grace period nothing has happened yet.
137
+ expect(el.editing).toBe(true);
138
+ await settle(160);
139
+ expect(el.editing).toBe(false);
140
+ expect(el.text).toBe('Ada Lovelace');
141
+ outside.remove();
142
+ unmountAll();
143
+ });
144
+
145
+ await it('a focus move WITHIN the widget is not leaving it', async () => {
146
+ const el = mount({ text: 'Ada' });
147
+ el.startEditing();
148
+ el.entry.value = 'Ada Lovelace';
149
+ el.dispatchEvent(new FocusEvent('focusout', { bubbles: false, relatedTarget: el }));
150
+ await settle(160);
151
+ expect(el.editing).toBe(true);
152
+ unmountAll();
153
+ });
154
+
155
+ await it('`editable="false"` stops editing without committing', () => {
156
+ const el = mount({ text: 'Ada' });
157
+ el.startEditing();
158
+ el.entry.value = 'Ada Lovelace';
159
+ el.editable = false;
160
+ expect(el.editing).toBe(false);
161
+ expect(el.text).toBe('Ada');
162
+ unmountAll();
163
+ });
164
+
165
+ await it('the label does not follow `text` while a draft is in the entry', () => {
166
+ const el = mount({ text: 'Ada' });
167
+ el.startEditing();
168
+ el.setAttribute('text', 'Grace Hopper');
169
+ expect(el.querySelector('.adw-editable-label-label')?.textContent).toBe('Ada');
170
+ expect(el.text).toBe('Grace Hopper');
171
+ unmountAll();
172
+ });
173
+ });
174
+
175
+ await describe('<gtk-editable-label> look', async () => {
176
+ await it('paints the view colours upstream gives the entry node', () => {
177
+ const el = mount({ text: 'Ada', editing: '' });
178
+ // `_labels.scss:94-97` writes `--view-bg-color` / `--view-fg-color` onto the
179
+ // entry, and the tokens move with the OS theme — so the row compares against a
180
+ // probe that resolves the same token rather than naming either colour.
181
+ const probe = document.createElement('div');
182
+ probe.style.backgroundColor = 'var(--view-bg-color)';
183
+ document.body.appendChild(probe);
184
+ expect(getComputedStyle(el.entry).backgroundColor).toBe(getComputedStyle(probe).backgroundColor);
185
+ expect(getComputedStyle(el.entry).color).toBe(getComputedStyle(el).color);
186
+ probe.remove();
187
+ unmountAll();
188
+ });
189
+ });
190
+ };
@@ -0,0 +1,173 @@
1
+ // DOM-level tests for <gtk-emoji-chooser>. The emoji table is data, so what these hold
2
+ // is the BEHAVIOUR around it: the prefix search of `match_tokens`, the section hiding of
3
+ // `update_headings` (including the "No Results Found" page), the recent list's cap and
4
+ // its one exception, and the section button that follows the scroll position.
5
+ import { describe, expect, it } from '@gjsify/unit';
6
+
7
+ import type { GtkEmojiChooser } from './elements/gtk-emoji-chooser.js';
8
+ import { EMOJI_CATEGORIES, EMOJI_MAX_RECENT } from './emoji-data.js';
9
+
10
+ function mount(attrs: Record<string, string> = {}): { el: GtkEmojiChooser; host: HTMLElement } {
11
+ const host = document.createElement('div');
12
+ document.body.appendChild(host);
13
+ const el = document.createElement('gtk-emoji-chooser') as GtkEmojiChooser;
14
+ for (const [name, value] of Object.entries(attrs)) el.setAttribute(name, value);
15
+ host.appendChild(el);
16
+ el.popup();
17
+ return { el, host };
18
+ }
19
+
20
+ function search(el: GtkEmojiChooser, text: string): void {
21
+ const entry = el.querySelector<HTMLInputElement>('.emoji-search-entry')!;
22
+ entry.value = text;
23
+ entry.dispatchEvent(new Event('input', { bubbles: true }));
24
+ }
25
+
26
+ /** The sections that are currently shown, in DOM order. */
27
+ function visibleSections(el: GtkEmojiChooser): string[] {
28
+ return [...el.querySelectorAll<HTMLElement>('.emoji-flowbox')]
29
+ .filter((box) => !box.hidden)
30
+ .map((box) => box.dataset.section ?? '');
31
+ }
32
+
33
+ export const GtkEmojiChooserTest = async () => {
34
+ await describe('<gtk-emoji-chooser> sections', async () => {
35
+ await it('hides the recent box while the list is empty, ahead of the nine categories', () => {
36
+ // The struct order of GtkEmojiChooser (gtkemojichooser.c:216-228) with recent
37
+ // ahead of them all, which is also the `.ui`'s document order — but
38
+ // `populate_recent_section` (:352) hides the recent box while the list is
39
+ // empty, so a fresh chooser shows the nine categories and the first pick
40
+ // puts the recent box ahead of them.
41
+ const { el, host } = mount();
42
+ expect(visibleSections(el)).toStrictEqual(EMOJI_CATEGORIES.map((c) => c.id));
43
+ el.querySelector<HTMLButtonElement>('.emoji-flowbox[data-section="people"] .emoji')!.click();
44
+ expect(visibleSections(el)).toStrictEqual(['recent', ...EMOJI_CATEGORIES.map((c) => c.id)]);
45
+ host.remove();
46
+ });
47
+
48
+ await it('one toolbar button per section, recent first and insensitised when empty', () => {
49
+ // populate_recent_section (gtkemojichooser.c:376-386) makes the recent button
50
+ // INSENSITIVE while its box is empty.
51
+ const { el, host } = mount();
52
+ const buttons = [...el.querySelectorAll<HTMLButtonElement>('.emoji-section')];
53
+ expect(buttons).toHaveLength(EMOJI_CATEGORIES.length + 1);
54
+ expect(buttons[0]?.dataset.section).toBe('recent');
55
+ expect(buttons[0]?.disabled).toBe(true);
56
+ expect(buttons[1]?.title).toBe('Smileys & People');
57
+ host.remove();
58
+ });
59
+
60
+ await it('has a heading per section and none for recent', () => {
61
+ const { el, host } = mount();
62
+ const headings = [...el.querySelectorAll('.emoji-heading')].map((h) => h.textContent);
63
+ expect(headings).toStrictEqual(EMOJI_CATEGORIES.map((c) => c.heading));
64
+ expect(el.querySelector('.emoji-recent-box')?.previousElementSibling).toBe(null);
65
+ host.remove();
66
+ });
67
+ });
68
+
69
+ await describe('<gtk-emoji-chooser> search', async () => {
70
+ await it('an empty search shows every section the list has', () => {
71
+ // `filter_func` (gtkemojichooser.c:896-913) — `if (text[0] == 0) goto out`.
72
+ // The recent box is still hidden: `update_headings` never shows it, only
73
+ // `add_recent_item` does.
74
+ const { el, host } = mount();
75
+ search(el, '');
76
+ expect(visibleSections(el)).toHaveLength(EMOJI_CATEGORIES.length);
77
+ expect(el.querySelector('.emoji-empty')?.hasAttribute('hidden')).toBe(true);
78
+ host.remove();
79
+ });
80
+
81
+ await it('matches a PREFIX of a name or keyword word, case-folded', () => {
82
+ // match_tokens (gtkemojichooser.c:871-894) — every TERM must prefix some token,
83
+ // and g_str_tokenize_and_fold case-folds both sides.
84
+ const { el, host } = mount();
85
+ search(el, 'grin');
86
+ const shown = [...el.querySelectorAll<HTMLButtonElement>('.emoji-flowbox[data-section="people"] .emoji')]
87
+ .filter((button) => !button.hidden)
88
+ .map((button) => button.getAttribute('aria-label'));
89
+ expect(shown?.length).toBeGreaterThan(0);
90
+ expect(shown).toContain('grinning face');
91
+ search(el, 'THUMBS');
92
+ expect(
93
+ [...el.querySelectorAll<HTMLButtonElement>('.emoji-flowbox .emoji')]
94
+ .filter((button) => !button.hidden)
95
+ .map((button) => button.getAttribute('aria-label')),
96
+ ).toContain('thumbs up');
97
+ host.remove();
98
+ });
99
+
100
+ await it('hides a section with no match, heading and all', () => {
101
+ // update_headings (gtkemojichooser.c:967-989).
102
+ const { el, host } = mount();
103
+ search(el, 'zzzz-no-such-emoji');
104
+ expect(visibleSections(el)).toStrictEqual([]);
105
+ expect(el.querySelector('.emoji-empty')?.hasAttribute('hidden')).toBe(false);
106
+ expect(el.querySelector('.emoji-empty-title')?.textContent).toBe('No Results Found');
107
+ host.remove();
108
+ });
109
+
110
+ await it('needs every term to match', () => {
111
+ const { el, host } = mount();
112
+ search(el, 'grinning face');
113
+ expect(visibleSections(el)).toContain('people');
114
+ search(el, 'grinning unicorn');
115
+ expect(visibleSections(el)).toStrictEqual([]);
116
+ host.remove();
117
+ });
118
+ });
119
+
120
+ await describe('<gtk-emoji-chooser> picking', async () => {
121
+ await it('emits emoji-picked with the Unicode sequence', () => {
122
+ // emoji_activated (gtkemojichooser.c:449-486) — the signal carries `text`, the
123
+ // LABEL's own contents, which is the code-point sequence and not the name.
124
+ const { el, host } = mount();
125
+ const seen: unknown[] = [];
126
+ el.addEventListener('emoji-picked', (event) => seen.push((event as CustomEvent).detail));
127
+ const first = el.querySelector<HTMLButtonElement>('.emoji-flowbox[data-section="people"] .emoji')!;
128
+ first.click();
129
+ expect(seen).toStrictEqual([{ text: '\u{1F600}' }]);
130
+ host.remove();
131
+ });
132
+
133
+ await it('records the pick in recents and enables its button', () => {
134
+ // add_recent_item (gtkemojichooser.c:390-447) — the section becomes visible and
135
+ // its button sensitive only on a NON-empty list.
136
+ const { el, host } = mount();
137
+ expect(el.recentEmoji).toStrictEqual([]);
138
+ el.querySelector<HTMLButtonElement>('.emoji-flowbox[data-section="people"] .emoji')!.click();
139
+ expect(el.recentEmoji).toHaveLength(1);
140
+ expect(el.querySelector('.emoji-recent-box')?.hasAttribute('hidden')).toBe(false);
141
+ expect(el.querySelector<HTMLButtonElement>('.emoji-section[data-section="recent"]')?.disabled).toBe(false);
142
+ host.remove();
143
+ });
144
+
145
+ await it('does not record a pick made FROM the recents', () => {
146
+ const { el, host } = mount();
147
+ el.querySelector<HTMLButtonElement>('.emoji-flowbox[data-section="people"] .emoji')!.click();
148
+ el.querySelector<HTMLButtonElement>('.emoji-recent-box .emoji')!.click();
149
+ expect(el.recentEmoji).toHaveLength(1);
150
+ host.remove();
151
+ });
152
+
153
+ await it('keeps the recents list at MAX_RECENT, newest first, no duplicates', () => {
154
+ const { el, host } = mount();
155
+ const names = ['\u{1F600}', '\u{1F603}', '\u{1F604}', '\u{1F601}'];
156
+ for (const text of names) el.pick({ text, name: text, keywords: [] });
157
+ // Re-picking the first entry moves it to the front rather than adding a
158
+ // second: `add_emoji (box, TRUE /* prepend */, …)` (gtkemojichooser.c:422).
159
+ el.pick({ text: '\u{1F600}', name: 'again', keywords: [] });
160
+ expect(el.recentEmoji.map((entry) => entry.text)).toStrictEqual([
161
+ '\u{1F600}',
162
+ '\u{1F601}',
163
+ '\u{1F604}',
164
+ '\u{1F603}',
165
+ ]);
166
+ for (let index = 0; index < EMOJI_MAX_RECENT + 5; index += 1) {
167
+ el.pick({ text: `x${index}`, name: `x${index}`, keywords: [] });
168
+ }
169
+ expect(el.recentEmoji).toHaveLength(EMOJI_MAX_RECENT);
170
+ host.remove();
171
+ });
172
+ });
173
+ };
@@ -70,4 +70,27 @@ export const GtkEntryTest = async () => {
70
70
  unmountAll();
71
71
  });
72
72
  });
73
+
74
+ await describe('<gtk-entry> GTK property spellings', async () => {
75
+ await it('reads placeholder-text, the Gtk.Entry property, and the older placeholder', () => {
76
+ expect(mountEntry({ 'placeholder-text': 'Search' }).input.placeholder).toBe('Search');
77
+ expect(mountEntry({ placeholder: 'Old' }).input.placeholder).toBe('Old');
78
+ expect(mountEntry({ 'placeholder-text': 'New', placeholder: 'Old' }).input.placeholder).toBe('New');
79
+ unmountAll();
80
+ });
81
+
82
+ await it('follows placeholder-text when it changes', () => {
83
+ const entry = mountEntry({ 'placeholder-text': 'a' });
84
+ entry.setAttribute('placeholder-text', 'b');
85
+ expect(entry.input.placeholder).toBe('b');
86
+ unmountAll();
87
+ });
88
+
89
+ await it('width-request is a minimum width; -1 and nonsense leave it natural', () => {
90
+ expect(mountEntry({ 'width-request': '280' }).style.minWidth).toBe('280px');
91
+ expect(mountEntry({ 'width-request': '-1' }).style.minWidth).toBe('');
92
+ expect(mountEntry({ 'width-request': 'x' }).style.minWidth).toBe('');
93
+ unmountAll();
94
+ });
95
+ });
73
96
  };