@gjsify/adwaita-web 0.54.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.
- package/dist/adwaita-web.css +8066 -3033
- package/dist/adwaita-web.css.map +1 -1
- package/lib/types/adw-application-window.spec.d.ts +1 -0
- package/lib/types/adw-bin.spec.d.ts +1 -0
- package/lib/types/adw-breakpoint-bin.spec.d.ts +1 -0
- package/lib/types/adw-clamp-scrollable.spec.d.ts +1 -0
- package/lib/types/adw-multi-layout-view.spec.d.ts +1 -0
- package/lib/types/adw-preferences-row.spec.d.ts +1 -0
- package/lib/types/adw-shortcuts.spec.d.ts +1 -0
- package/lib/types/adw-tab-bar.spec.d.ts +1 -0
- package/lib/types/adw-tab-button.spec.d.ts +1 -0
- package/lib/types/adw-tab-overview.spec.d.ts +1 -0
- package/lib/types/adw-view-switcher-sidebar.spec.d.ts +1 -0
- package/lib/types/adw-window.spec.d.ts +1 -0
- package/lib/types/breakpoints.d.ts +14 -3
- package/lib/types/construct-vectors.spec.d.ts +1 -0
- package/lib/types/elements/adw-application-window.d.ts +27 -0
- package/lib/types/elements/adw-bin.d.ts +17 -0
- package/lib/types/elements/adw-breakpoint-bin.d.ts +67 -0
- package/lib/types/elements/adw-clamp-scrollable.d.ts +38 -0
- package/lib/types/elements/adw-dialog.d.ts +10 -0
- package/lib/types/elements/adw-layout-slot.d.ts +19 -0
- package/lib/types/elements/adw-multi-layout-view.d.ts +101 -0
- package/lib/types/elements/adw-preferences-row.d.ts +31 -0
- package/lib/types/elements/adw-shortcuts-dialog.d.ts +13 -0
- package/lib/types/elements/adw-shortcuts-item.d.ts +36 -0
- package/lib/types/elements/adw-shortcuts-section.d.ts +14 -0
- package/lib/types/elements/adw-sidebar.d.ts +5 -0
- package/lib/types/elements/adw-switch-row.d.ts +3 -0
- package/lib/types/elements/adw-tab-bar.d.ts +108 -0
- package/lib/types/elements/adw-tab-button.d.ts +32 -0
- package/lib/types/elements/adw-tab-overview.d.ts +199 -0
- package/lib/types/elements/adw-tab-view.d.ts +19 -23
- package/lib/types/elements/adw-view-switcher-sidebar.d.ts +84 -0
- package/lib/types/elements/adw-window.d.ts +40 -0
- package/lib/types/elements/gtk-about-dialog.d.ts +146 -0
- package/lib/types/elements/gtk-application-window.d.ts +34 -0
- package/lib/types/elements/gtk-aspect-frame.d.ts +27 -0
- package/lib/types/elements/gtk-button.d.ts +9 -0
- package/lib/types/elements/gtk-calendar.d.ts +159 -0
- package/lib/types/elements/gtk-center-box.d.ts +39 -0
- package/lib/types/elements/gtk-color-dialog-button.d.ts +69 -0
- package/lib/types/elements/gtk-column-view.d.ts +50 -0
- package/lib/types/elements/gtk-drag-icon.d.ts +78 -0
- package/lib/types/elements/gtk-drawing-area.d.ts +95 -0
- package/lib/types/elements/gtk-editable-label.d.ts +33 -0
- package/lib/types/elements/gtk-emoji-chooser.d.ts +84 -0
- package/lib/types/elements/gtk-entry.d.ts +20 -1
- package/lib/types/elements/gtk-expander.d.ts +49 -0
- package/lib/types/elements/gtk-fixed.d.ts +13 -0
- package/lib/types/elements/gtk-flow-box-child.d.ts +14 -0
- package/lib/types/elements/gtk-flow-box.d.ts +87 -0
- package/lib/types/elements/gtk-font-dialog-button.d.ts +83 -0
- package/lib/types/elements/gtk-frame.d.ts +24 -0
- package/lib/types/elements/gtk-gl-area.d.ts +136 -0
- package/lib/types/elements/gtk-graphics-offload.d.ts +24 -0
- package/lib/types/elements/gtk-grid-view.d.ts +39 -0
- package/lib/types/elements/gtk-grid.d.ts +27 -0
- package/lib/types/elements/gtk-header-bar.d.ts +60 -0
- package/lib/types/elements/gtk-inscription.d.ts +64 -0
- package/lib/types/elements/gtk-label.d.ts +3 -0
- package/lib/types/elements/gtk-level-bar.d.ts +59 -0
- package/lib/types/elements/gtk-link-button.d.ts +29 -0
- package/lib/types/elements/gtk-list-box-row.d.ts +54 -0
- package/lib/types/elements/gtk-list-box.d.ts +100 -0
- package/lib/types/elements/gtk-list-view.d.ts +73 -0
- package/lib/types/elements/gtk-media-controls.d.ts +142 -0
- package/lib/types/elements/gtk-notebook.d.ts +148 -0
- package/lib/types/elements/gtk-overlay.d.ts +22 -0
- package/lib/types/elements/gtk-page-setup-unix-dialog.d.ts +87 -0
- package/lib/types/elements/gtk-paned.d.ts +121 -0
- package/lib/types/elements/gtk-password-entry.d.ts +27 -0
- package/lib/types/elements/gtk-picture.d.ts +66 -0
- package/lib/types/elements/gtk-popover-bin.d.ts +82 -0
- package/lib/types/elements/gtk-popover-menu-bar.d.ts +66 -0
- package/lib/types/elements/gtk-popover-menu.d.ts +83 -0
- package/lib/types/elements/gtk-popover.d.ts +28 -3
- package/lib/types/elements/gtk-print-unix-dialog.d.ts +167 -0
- package/lib/types/elements/gtk-revealer.d.ts +55 -0
- package/lib/types/elements/gtk-scale-button.d.ts +59 -0
- package/lib/types/elements/gtk-scale.d.ts +98 -0
- package/lib/types/elements/gtk-scrollbar.d.ts +124 -0
- package/lib/types/elements/gtk-scrolled-window.d.ts +121 -0
- package/lib/types/elements/gtk-search-bar.d.ts +39 -0
- package/lib/types/elements/gtk-search-entry.d.ts +27 -0
- package/lib/types/elements/gtk-separator.d.ts +10 -0
- package/lib/types/elements/gtk-spin-button.d.ts +94 -0
- package/lib/types/elements/gtk-spinner.d.ts +14 -0
- package/lib/types/elements/gtk-stack-sidebar.d.ts +43 -0
- package/lib/types/elements/gtk-stack-switcher.d.ts +58 -0
- package/lib/types/elements/gtk-stack.d.ts +154 -0
- package/lib/types/elements/gtk-switch.d.ts +4 -1
- package/lib/types/elements/gtk-text-view.d.ts +62 -0
- package/lib/types/elements/gtk-text.d.ts +45 -0
- package/lib/types/elements/gtk-toggle-button.d.ts +45 -0
- package/lib/types/elements/gtk-tree-expander.d.ts +38 -0
- package/lib/types/elements/gtk-video.d.ts +97 -0
- package/lib/types/elements/gtk-viewport.d.ts +84 -0
- package/lib/types/elements/gtk-window-controls.d.ts +80 -0
- package/lib/types/elements/gtk-window-handle.d.ts +39 -0
- package/lib/types/elements/gtk-window.d.ts +97 -0
- package/lib/types/elements/justification.d.ts +2 -0
- package/lib/types/elements/popover-menu.d.ts +18 -0
- package/lib/types/elements/roving-focus.d.ts +29 -1
- package/lib/types/emoji-data.d.ts +38 -0
- package/lib/types/font-description.d.ts +96 -0
- package/lib/types/gtk-about-dialog.spec.d.ts +1 -0
- package/lib/types/gtk-action-bar.spec.d.ts +1 -0
- package/lib/types/gtk-application-window.spec.d.ts +1 -0
- package/lib/types/gtk-aspect-frame.spec.d.ts +1 -0
- package/lib/types/gtk-calendar.spec.d.ts +1 -0
- package/lib/types/gtk-center-box.spec.d.ts +1 -0
- package/lib/types/gtk-color-dialog-button.spec.d.ts +1 -0
- package/lib/types/gtk-column-view.spec.d.ts +1 -0
- package/lib/types/gtk-drag-icon.spec.d.ts +1 -0
- package/lib/types/gtk-drawing-area.spec.d.ts +1 -0
- package/lib/types/gtk-editable-label.spec.d.ts +1 -0
- package/lib/types/gtk-emoji-chooser.spec.d.ts +1 -0
- package/lib/types/gtk-expander.spec.d.ts +1 -0
- package/lib/types/gtk-fixed.spec.d.ts +1 -0
- package/lib/types/gtk-flow-box.spec.d.ts +1 -0
- package/lib/types/gtk-font-dialog-button.spec.d.ts +1 -0
- package/lib/types/gtk-frame.spec.d.ts +1 -0
- package/lib/types/gtk-gl-area.spec.d.ts +1 -0
- package/lib/types/gtk-graphics-offload.spec.d.ts +1 -0
- package/lib/types/gtk-grid-view.spec.d.ts +1 -0
- package/lib/types/gtk-grid.spec.d.ts +1 -0
- package/lib/types/gtk-header-bar.spec.d.ts +1 -0
- package/lib/types/gtk-inscription.spec.d.ts +1 -0
- package/lib/types/gtk-level-bar.spec.d.ts +1 -0
- package/lib/types/gtk-link-button.spec.d.ts +1 -0
- package/lib/types/gtk-list-box.spec.d.ts +1 -0
- package/lib/types/gtk-list-view.spec.d.ts +1 -0
- package/lib/types/gtk-media-controls.spec.d.ts +1 -0
- package/lib/types/gtk-notebook.spec.d.ts +1 -0
- package/lib/types/gtk-overlay.spec.d.ts +1 -0
- package/lib/types/gtk-paned.spec.d.ts +1 -0
- package/lib/types/gtk-password-entry.spec.d.ts +1 -0
- package/lib/types/gtk-picture.spec.d.ts +1 -0
- package/lib/types/gtk-popover-bin.spec.d.ts +1 -0
- package/lib/types/gtk-popover-menu-bar.spec.d.ts +1 -0
- package/lib/types/gtk-popover-menu.spec.d.ts +1 -0
- package/lib/types/gtk-popover-role.spec.d.ts +1 -0
- package/lib/types/gtk-popover.spec.d.ts +1 -0
- package/lib/types/gtk-print-dialogs.spec.d.ts +1 -0
- package/lib/types/gtk-revealer.spec.d.ts +1 -0
- package/lib/types/gtk-scale-button.spec.d.ts +1 -0
- package/lib/types/gtk-scale.spec.d.ts +1 -0
- package/lib/types/gtk-scrollbar.spec.d.ts +1 -0
- package/lib/types/gtk-scrolled-window.spec.d.ts +1 -0
- package/lib/types/gtk-search-bar.spec.d.ts +1 -0
- package/lib/types/gtk-search-entry.spec.d.ts +1 -0
- package/lib/types/gtk-separator.spec.d.ts +1 -0
- package/lib/types/gtk-spin-button.spec.d.ts +1 -0
- package/lib/types/gtk-spinner.spec.d.ts +1 -0
- package/lib/types/gtk-stack-sidebar.spec.d.ts +1 -0
- package/lib/types/gtk-stack-switcher.spec.d.ts +1 -0
- package/lib/types/gtk-stack.spec.d.ts +1 -0
- package/lib/types/gtk-text-view.spec.d.ts +1 -0
- package/lib/types/gtk-text.spec.d.ts +1 -0
- package/lib/types/gtk-toggle-button.spec.d.ts +1 -0
- package/lib/types/gtk-tree-expander.spec.d.ts +1 -0
- package/lib/types/gtk-video.spec.d.ts +1 -0
- package/lib/types/gtk-viewport.spec.d.ts +1 -0
- package/lib/types/gtk-window-controls.spec.d.ts +1 -0
- package/lib/types/gtk-window-handle.spec.d.ts +1 -0
- package/lib/types/gtk-window.spec.d.ts +1 -0
- package/lib/types/index.d.ts +4 -1
- package/lib/types/list-view-dom.d.ts +44 -0
- package/lib/types/namespace/adw.d.ts +12 -0
- package/lib/types/namespace/gtk.d.ts +61 -0
- package/lib/types/page-setup.d.ts +100 -0
- package/lib/types/scroll-shading.d.ts +15 -1
- package/lib/types/shared-tree-builder.d.ts +30 -2
- package/lib/types/signals.d.ts +4 -0
- package/lib/types/styles.generated.d.ts +1 -1
- package/lib/types/tab-chip.d.ts +73 -0
- package/lib/types/template-classes.d.ts +11 -0
- package/package.json +12 -8
- package/scss/_action_row.scss +2 -2
- package/scss/_application_window.scss +26 -0
- package/scss/_aspect_frame.scss +25 -0
- package/scss/_bin.scss +26 -0
- package/scss/_button.scss +45 -0
- package/scss/_calendar.scss +176 -0
- package/scss/_center_box.scss +25 -0
- package/scss/_checks.scss +6 -1
- package/scss/_clamp_scrollable.scss +28 -0
- package/scss/_color_button.scss +67 -0
- package/scss/_column_view.scss +126 -0
- package/scss/_drag_icon.scss +55 -0
- package/scss/_drawing.scss +85 -0
- package/scss/_emoji_chooser.scss +213 -0
- package/scss/_entry.scss +13 -5
- package/scss/_entry_row.scss +1 -1
- package/scss/_expander.scss +113 -0
- package/scss/_fixed.scss +29 -0
- package/scss/_flow_box.scss +94 -0
- package/scss/_font_button.scss +63 -0
- package/scss/_frame.scss +41 -0
- package/scss/_grid.scss +19 -0
- package/scss/_grid_view.scss +46 -0
- package/scss/_gtk_about_dialog.scss +186 -0
- package/scss/_headerbar.scss +132 -0
- package/scss/_icons.generated.scss +186 -0
- package/scss/_inscription.scss +125 -0
- package/scss/_level_bar.scss +190 -0
- package/scss/_link.scss +82 -0
- package/scss/_list_box.scss +88 -0
- package/scss/_list_view.scss +96 -0
- package/scss/_media_controls.scss +139 -0
- package/scss/_overlay.scss +68 -0
- package/scss/_paned.scss +130 -0
- package/scss/_password_entry.scss +83 -0
- package/scss/_picture.scss +80 -0
- package/scss/_popover_bin.scss +53 -0
- package/scss/_popover_menu_bar.scss +104 -0
- package/scss/_preferences_row.scss +51 -0
- package/scss/_print_dialogs.scss +328 -0
- package/scss/_reset.scss +21 -19
- package/scss/_revealer.scss +37 -0
- package/scss/_row.scss +20 -5
- package/scss/_scale.scss +298 -0
- package/scss/_scale_button.scss +167 -0
- package/scss/_scrollbar.scss +230 -0
- package/scss/_scrolled_window.scss +131 -0
- package/scss/_search_bar.scss +118 -0
- package/scss/_search_entry.scss +86 -0
- package/scss/_separator.scss +44 -0
- package/scss/_shortcuts_dialog.scss +100 -0
- package/scss/_spin_button.scss +172 -0
- package/scss/_spin_row.scss +56 -8
- package/scss/_spinner.scss +85 -4
- package/scss/_stack.scss +729 -0
- package/scss/_switch.scss +13 -5
- package/scss/_switch_row.scss +10 -0
- package/scss/_tab_bar.scss +317 -0
- package/scss/_tab_button.scss +98 -0
- package/scss/_tab_overview.scss +483 -0
- package/scss/_text.scss +182 -0
- package/scss/_theme.scss +1 -1
- package/scss/_tree_expander.scss +62 -0
- package/scss/_variables.scss +1 -1
- package/scss/_video.scss +146 -0
- package/scss/_view_switcher_sidebar.scss +112 -0
- package/scss/_viewport.scss +50 -0
- package/scss/_window.scss +118 -1
- package/scss/_window_handle.scss +20 -0
- package/scss/adwaita-skin.scss +109 -0
- package/src/adw-action-rows.spec.ts +76 -0
- package/src/adw-application-window.spec.ts +118 -0
- package/src/adw-bin.spec.ts +75 -0
- package/src/adw-breakpoint-bin.spec.ts +324 -0
- package/src/adw-clamp-scrollable.spec.ts +189 -0
- package/src/adw-multi-layout-view.spec.ts +243 -0
- package/src/adw-preferences-row.spec.ts +164 -0
- package/src/adw-row-state.spec.ts +142 -0
- package/src/adw-shortcuts.spec.ts +155 -0
- package/src/adw-tab-bar.spec.ts +287 -0
- package/src/adw-tab-button.spec.ts +101 -0
- package/src/adw-tab-overview.spec.ts +443 -0
- package/src/adw-view-switcher-sidebar.spec.ts +281 -0
- package/src/adw-window.spec.ts +129 -0
- package/src/breakpoints.ts +25 -8
- package/src/capabilities.d.mts +3 -0
- package/src/capabilities.mjs +18 -0
- package/src/connect-lifecycle.spec.ts +12 -0
- package/src/construct-vectors.spec.ts +175 -0
- package/src/elements/adw-application-window.ts +154 -0
- package/src/elements/adw-bin.ts +101 -0
- package/src/elements/adw-breakpoint-bin.ts +287 -0
- package/src/elements/adw-clamp-scrollable.ts +207 -0
- package/src/elements/adw-dialog.ts +19 -0
- package/src/elements/adw-layout-slot.ts +95 -0
- package/src/elements/adw-multi-layout-view.ts +326 -0
- package/src/elements/adw-preferences-row.ts +150 -0
- package/src/elements/adw-shortcuts-dialog.ts +52 -0
- package/src/elements/adw-shortcuts-item.ts +171 -0
- package/src/elements/adw-shortcuts-section.ts +86 -0
- package/src/elements/adw-sidebar.ts +42 -3
- package/src/elements/adw-switch-row.ts +28 -1
- package/src/elements/adw-tab-bar.ts +529 -0
- package/src/elements/adw-tab-button.ts +194 -0
- package/src/elements/adw-tab-overview.ts +1011 -0
- package/src/elements/adw-tab-view.ts +66 -145
- package/src/elements/adw-toggle-group.ts +20 -5
- package/src/elements/adw-view-stack.ts +18 -0
- package/src/elements/adw-view-switcher-sidebar.ts +332 -0
- package/src/elements/adw-window.ts +185 -11
- package/src/elements/checks.ts +35 -8
- package/src/elements/gtk-about-dialog.ts +811 -0
- package/src/elements/gtk-application-window.ts +108 -0
- package/src/elements/gtk-aspect-frame.ts +160 -0
- package/src/elements/gtk-button.ts +12 -0
- package/src/elements/gtk-calendar.ts +757 -0
- package/src/elements/gtk-center-box.ts +184 -0
- package/src/elements/gtk-color-dialog-button.ts +278 -0
- package/src/elements/gtk-column-view.ts +320 -0
- package/src/elements/gtk-drag-icon.ts +257 -0
- package/src/elements/gtk-drawing-area.ts +303 -0
- package/src/elements/gtk-editable-label.ts +204 -0
- package/src/elements/gtk-emoji-chooser.ts +542 -0
- package/src/elements/gtk-entry.ts +39 -5
- package/src/elements/gtk-expander.ts +301 -0
- package/src/elements/gtk-fixed.ts +99 -0
- package/src/elements/gtk-flow-box-child.ts +68 -0
- package/src/elements/gtk-flow-box.ts +480 -0
- package/src/elements/gtk-font-dialog-button.ts +432 -0
- package/src/elements/gtk-frame.ts +174 -0
- package/src/elements/gtk-gl-area.ts +476 -0
- package/src/elements/gtk-graphics-offload.ts +130 -0
- package/src/elements/gtk-grid-view.ts +168 -0
- package/src/elements/gtk-grid.ts +127 -0
- package/src/elements/gtk-header-bar.ts +273 -0
- package/src/elements/gtk-inscription.ts +387 -0
- package/src/elements/gtk-label.ts +19 -11
- package/src/elements/gtk-level-bar.ts +323 -0
- package/src/elements/gtk-link-button.ts +138 -0
- package/src/elements/gtk-list-box-row.ts +195 -0
- package/src/elements/gtk-list-box.ts +413 -0
- package/src/elements/gtk-list-view.ts +370 -0
- package/src/elements/gtk-media-controls.ts +509 -0
- package/src/elements/gtk-notebook.ts +664 -0
- package/src/elements/gtk-overlay.ts +149 -0
- package/src/elements/gtk-page-setup-unix-dialog.ts +420 -0
- package/src/elements/gtk-paned.ts +597 -0
- package/src/elements/gtk-password-entry.ts +170 -0
- package/src/elements/gtk-picture.ts +254 -0
- package/src/elements/gtk-popover-bin.ts +332 -0
- package/src/elements/gtk-popover-menu-bar.ts +355 -0
- package/src/elements/gtk-popover-menu.ts +339 -0
- package/src/elements/gtk-popover.ts +92 -10
- package/src/elements/gtk-print-unix-dialog.ts +989 -0
- package/src/elements/gtk-progress-bar.ts +24 -11
- package/src/elements/gtk-revealer.ts +406 -0
- package/src/elements/gtk-scale-button.ts +358 -0
- package/src/elements/gtk-scale.ts +492 -0
- package/src/elements/gtk-scrollbar.ts +528 -0
- package/src/elements/gtk-scrolled-window.ts +642 -0
- package/src/elements/gtk-search-bar.ts +200 -0
- package/src/elements/gtk-search-entry.ts +172 -0
- package/src/elements/gtk-separator.ts +52 -0
- package/src/elements/gtk-spin-button.ts +475 -0
- package/src/elements/gtk-spinner.ts +126 -0
- package/src/elements/gtk-stack-sidebar.ts +212 -0
- package/src/elements/gtk-stack-switcher.ts +279 -0
- package/src/elements/gtk-stack.ts +748 -0
- package/src/elements/gtk-switch.ts +52 -19
- package/src/elements/gtk-text-view.ts +290 -0
- package/src/elements/gtk-text.ts +219 -0
- package/src/elements/gtk-toggle-button.ts +239 -0
- package/src/elements/gtk-tree-expander.ts +243 -0
- package/src/elements/gtk-video.ts +423 -0
- package/src/elements/gtk-viewport.ts +277 -0
- package/src/elements/gtk-window-controls.ts +341 -0
- package/src/elements/gtk-window-handle.ts +221 -0
- package/src/elements/gtk-window.ts +366 -0
- package/src/elements/justification.ts +24 -0
- package/src/elements/popover-menu.ts +24 -0
- package/src/elements/roving-focus.ts +53 -21
- package/src/emoji-data.ts +577 -0
- package/src/entry-rows.spec.ts +31 -0
- package/src/font-description.ts +238 -0
- package/src/gtk-about-dialog.spec.ts +250 -0
- package/src/gtk-action-bar.spec.ts +89 -0
- package/src/gtk-application-window.spec.ts +107 -0
- package/src/gtk-aspect-frame.spec.ts +114 -0
- package/src/gtk-calendar.spec.ts +589 -0
- package/src/gtk-center-box.spec.ts +125 -0
- package/src/gtk-color-dialog-button.spec.ts +141 -0
- package/src/gtk-column-view.spec.ts +154 -0
- package/src/gtk-drag-icon.spec.ts +190 -0
- package/src/gtk-drawing-area.spec.ts +211 -0
- package/src/gtk-editable-label.spec.ts +190 -0
- package/src/gtk-emoji-chooser.spec.ts +173 -0
- package/src/gtk-entry.spec.ts +23 -0
- package/src/gtk-expander.spec.ts +170 -0
- package/src/gtk-fixed.spec.ts +97 -0
- package/src/gtk-flow-box.spec.ts +289 -0
- package/src/gtk-font-dialog-button.spec.ts +166 -0
- package/src/gtk-frame.spec.ts +119 -0
- package/src/gtk-gl-area.spec.ts +361 -0
- package/src/gtk-graphics-offload.spec.ts +119 -0
- package/src/gtk-grid-view.spec.ts +106 -0
- package/src/gtk-grid.spec.ts +122 -0
- package/src/gtk-header-bar.spec.ts +202 -0
- package/src/gtk-inscription.spec.ts +235 -0
- package/src/gtk-level-bar.spec.ts +199 -0
- package/src/gtk-link-button.spec.ts +117 -0
- package/src/gtk-list-box.spec.ts +422 -0
- package/src/gtk-list-view.spec.ts +203 -0
- package/src/gtk-media-controls.spec.ts +295 -0
- package/src/gtk-notebook.spec.ts +546 -0
- package/src/gtk-overlay.spec.ts +141 -0
- package/src/gtk-paned.spec.ts +267 -0
- package/src/gtk-password-entry.spec.ts +150 -0
- package/src/gtk-picture.spec.ts +172 -0
- package/src/gtk-popover-bin.spec.ts +229 -0
- package/src/gtk-popover-menu-bar.spec.ts +296 -0
- package/src/gtk-popover-menu.spec.ts +261 -0
- package/src/gtk-popover-role.spec.ts +138 -0
- package/src/gtk-popover.spec.ts +264 -0
- package/src/gtk-print-dialogs.spec.ts +251 -0
- package/src/gtk-revealer.spec.ts +164 -0
- package/src/gtk-scale-button.spec.ts +218 -0
- package/src/gtk-scale.spec.ts +213 -0
- package/src/gtk-scrollbar.spec.ts +244 -0
- package/src/gtk-scrolled-window.spec.ts +262 -0
- package/src/gtk-search-bar.spec.ts +179 -0
- package/src/gtk-search-entry.spec.ts +142 -0
- package/src/gtk-separator.spec.ts +74 -0
- package/src/gtk-spin-button.spec.ts +186 -0
- package/src/gtk-spinner.spec.ts +130 -0
- package/src/gtk-stack-sidebar.spec.ts +217 -0
- package/src/gtk-stack-switcher.spec.ts +249 -0
- package/src/gtk-stack.spec.ts +432 -0
- package/src/gtk-text-view.spec.ts +189 -0
- package/src/gtk-text.spec.ts +163 -0
- package/src/gtk-toggle-button.spec.ts +186 -0
- package/src/gtk-tree-expander.spec.ts +201 -0
- package/src/gtk-video.spec.ts +240 -0
- package/src/gtk-viewport.spec.ts +140 -0
- package/src/gtk-window-controls.spec.ts +232 -0
- package/src/gtk-window-handle.spec.ts +158 -0
- package/src/gtk-window.spec.ts +190 -0
- package/src/index.ts +9 -1
- package/src/keyboard-operable.spec.ts +33 -1
- package/src/list-view-dom.ts +121 -0
- package/src/namespace/adw.ts +12 -0
- package/src/namespace/gtk.ts +61 -0
- package/src/page-setup.ts +222 -0
- package/src/scroll-shading.ts +18 -1
- package/src/shared-tree-builder.ts +263 -21
- package/src/signals.ts +15 -0
- package/src/style-classes.spec.ts +9 -8
- package/src/styles.generated.ts +1 -1
- package/src/tab-chip.ts +200 -0
- package/src/template-classes.ts +45 -0
- package/src/test.browser.mts +152 -0
|
@@ -0,0 +1,542 @@
|
|
|
1
|
+
// <gtk-emoji-chooser> — the browser counterpart of `GtkEmojiChooser`, GTK's emoji
|
|
2
|
+
// popover: a search entry over a scrolled list of emoji in ten sections (recent first),
|
|
3
|
+
// with a toolbar of section buttons that both JUMP to a section (`scroll_to_section`) and
|
|
4
|
+
// follow the scroll position (`adj_value_changed`), and an `emoji-picked` signal.
|
|
5
|
+
//
|
|
6
|
+
// The SURFACE is `<gtk-popover>`'s — this element extends `GtkPopover`, so the surface
|
|
7
|
+
// node, the Escape dismissal, the outside-click popdown and the focus return are the ones
|
|
8
|
+
// `gtk-popover.ts` already owns, and the CSS-node contract upstream names (`popover`,
|
|
9
|
+
// `.emoji-searchbar`, `.emoji-toolbar`, `.emoji-section`, `emoji`) is reproduced by the
|
|
10
|
+
// class names below rather than by a second surface.
|
|
11
|
+
//
|
|
12
|
+
// WHAT IS PORTED FROM `gtkemojichooser.c`
|
|
13
|
+
//
|
|
14
|
+
// · the CSS node tree (the `ui/gtkemojichooser.ui` template): a searchbar box holding
|
|
15
|
+
// the entry, a stack of `list` / `empty`, a scrolled `.view` holding one flow box per
|
|
16
|
+
// section with a heading label above each, and a toolbar of `.emoji-section` buttons;
|
|
17
|
+
// · `filter_func` (:896-958) + `match_tokens` (:871-894): the search text is tokenised,
|
|
18
|
+
// case-folded, and an emoji matches when EVERY term is a PREFIX of a token of its name
|
|
19
|
+
// or of one of its keywords. Empty text matches everything
|
|
20
|
+
// (`if (text[0] == 0) goto out`);
|
|
21
|
+
// · `search_changed` (:991) + `update_headings` (:967): a section with no match hides
|
|
22
|
+
// its heading AND its emoji, and a search that empties every section shows the
|
|
23
|
+
// "No Results Found" page instead of the list;
|
|
24
|
+
// · `emoji_activated` (:449-486): picking an emoji emits `emoji-picked` with the text,
|
|
25
|
+
// records it in the recent list unless it came FROM the recent list, and pops the
|
|
26
|
+
// popover down — unless Ctrl is held, which keeps it open to pick several;
|
|
27
|
+
// · `add_recent_item` (:390-447): the recent list is at most `MAX_RECENT` (21) entries,
|
|
28
|
+
// an entry already in it moves to the front, and the section's button becomes
|
|
29
|
+
// sensitive and the section visible (both of which it only does on a NON-empty list);
|
|
30
|
+
// · `gtk_emoji_chooser_scroll_section` (:1198) behind Ctrl+N / Ctrl+P
|
|
31
|
+
// (`gtk_widget_class_add_binding_action`, :1452-1456) and `activate_search` (:1019),
|
|
32
|
+
// which picks the first result of the first non-empty section;
|
|
33
|
+
// · `adj_value_changed` (:814-870): the section whose heading the scroll position has
|
|
34
|
+
// passed is the CHECKED one and every other button is unchecked;
|
|
35
|
+
// · `gtk_emoji_chooser_show` (:1122-1133): showing the popover resets the scroll
|
|
36
|
+
// position to 0 and CLEARS the search text, which is why re-opening starts over;
|
|
37
|
+
// · `has_variations` (:489) / `show_variations` (:508-548): a SECOND click on an emoji
|
|
38
|
+
// that carries a skin-tone slot opens the six variants beside it, and picking one of
|
|
39
|
+
// them picks that variant. GTK opens them with a long press or the third mouse button;
|
|
40
|
+
// a second click is the same affordance where a long press is not available.
|
|
41
|
+
//
|
|
42
|
+
// NOT PORTED, and it is not a browser limit: the emoji table itself. GTK's is a compiled
|
|
43
|
+
// 1.2 MB GVariant per language, so `src/emoji-data.ts` carries a curated table with the
|
|
44
|
+
// same shape; every rule above runs on it unchanged.
|
|
45
|
+
//
|
|
46
|
+
// Attributes: `open`, `position`, `align`, `role`, `menu` from `<gtk-popover>`. This
|
|
47
|
+
// element adds NONE of its own — `GtkEmojiChooser` declares no properties in the GIR.
|
|
48
|
+
//
|
|
49
|
+
// Events: `emoji-picked` (bubbling CustomEvent, detail `{ text }`), plus the popover's
|
|
50
|
+
// `notify::open` and `popover-item-activated`.
|
|
51
|
+
//
|
|
52
|
+
// A11y: the surface is a `listbox` (a grid of choices, not a menu), each emoji is a
|
|
53
|
+
// `button` labelled with the English name — the name the search reads — and the section
|
|
54
|
+
// headings are headings rather than presentational labels.
|
|
55
|
+
//
|
|
56
|
+
// Reference: refs/gtk/gtk/gtkemojichooser.c (every rule above, by line)
|
|
57
|
+
// Reference: refs/gtk/gtk/ui/gtkemojichooser.ui (the node tree, the headings, the tooltips)
|
|
58
|
+
// Reference: refs/libadwaita/src/stylesheet/widgets/_emoji-chooser.scss
|
|
59
|
+
// Copyright (c) 2021-2024 The GTK authors. LGPLv2.1+.
|
|
60
|
+
// Modifications: Implemented as a Web Component for @gjsify/adwaita-web; the surface is
|
|
61
|
+
// <gtk-popover>; the emoji table is packages/web/adwaita-web/src/emoji-data.ts.
|
|
62
|
+
|
|
63
|
+
import {
|
|
64
|
+
EMOJI_CATEGORIES,
|
|
65
|
+
EMOJI_MAX_RECENT,
|
|
66
|
+
EMOJI_SKIN_TONES,
|
|
67
|
+
type EmojiEntry,
|
|
68
|
+
emojiTooltip,
|
|
69
|
+
hasVariations,
|
|
70
|
+
} from '../emoji-data.js';
|
|
71
|
+
|
|
72
|
+
import { createGtkImage } from './gtk-image.js';
|
|
73
|
+
import { GtkPopover } from './gtk-popover.js';
|
|
74
|
+
|
|
75
|
+
/** One section of the list: its heading, its flow box and its toolbar button. */
|
|
76
|
+
interface EmojiSection {
|
|
77
|
+
readonly id: string;
|
|
78
|
+
readonly heading: HTMLElement | null;
|
|
79
|
+
readonly box: HTMLDivElement;
|
|
80
|
+
readonly button: HTMLButtonElement | null;
|
|
81
|
+
/** `GtkFlowBox`'s `empty` latch: set by `invalidate_section`, cleared by any match. */
|
|
82
|
+
empty: boolean;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** `BOX_SPACE` (gtkemojichooser.c:92) — the gap `scroll_to_section` leaves above a heading. */
|
|
86
|
+
const BOX_SPACE = 6;
|
|
87
|
+
|
|
88
|
+
export class GtkEmojiChooser extends GtkPopover {
|
|
89
|
+
private _built = false;
|
|
90
|
+
private _entry!: HTMLInputElement;
|
|
91
|
+
private _listEl!: HTMLDivElement;
|
|
92
|
+
private _emptyEl!: HTMLElement;
|
|
93
|
+
private _sections = new Map<string, EmojiSection>();
|
|
94
|
+
/** The entry a button was built from — `emoji-data` has no reverse lookup. */
|
|
95
|
+
private _entryOf = new WeakMap<HTMLElement, EmojiEntry>();
|
|
96
|
+
/** `recently-used-emoji` — the GSettings key, held in memory here (see the header). */
|
|
97
|
+
private _recent: EmojiEntry[] = [];
|
|
98
|
+
/**
|
|
99
|
+
* The Ctrl state `should_close` reads off the keyboard (:433-448). A DOM event's
|
|
100
|
+
* `ctrlKey` says what the key that caused IT was, and a `click` synthesised from a
|
|
101
|
+
* keyboard activation carries no modifier at all, so the state is tracked from the key
|
|
102
|
+
* events themselves — which is what `gdk_device_get_modifier_state` answers.
|
|
103
|
+
*/
|
|
104
|
+
private _ctrl = false;
|
|
105
|
+
|
|
106
|
+
connectedCallback(): void {
|
|
107
|
+
super.connectedCallback();
|
|
108
|
+
if (this._built) return;
|
|
109
|
+
this._built = true;
|
|
110
|
+
|
|
111
|
+
// A grid of choices, not a menu: `role="listbox"` is what the popover's own rows
|
|
112
|
+
// (`popover-item-activated`) and the a11y layer are told to expect.
|
|
113
|
+
if (!this.hasAttribute('role')) this.setAttribute('role', 'listbox');
|
|
114
|
+
|
|
115
|
+
const root = document.createElement('div');
|
|
116
|
+
root.className = 'emoji-picker-root';
|
|
117
|
+
root.appendChild(this._buildSearchbar());
|
|
118
|
+
|
|
119
|
+
this._listEl = document.createElement('div');
|
|
120
|
+
this._listEl.className = 'emoji-list';
|
|
121
|
+
this._emptyEl = this._buildEmptyPage();
|
|
122
|
+
const stack = document.createElement('div');
|
|
123
|
+
stack.className = 'emoji-stack';
|
|
124
|
+
stack.append(this._listEl, this._emptyEl);
|
|
125
|
+
root.appendChild(stack);
|
|
126
|
+
|
|
127
|
+
this.replaceChildren(root);
|
|
128
|
+
|
|
129
|
+
// DOCUMENT ORDER IS SECTION ORDER: `recent.box` first, because the `.ui` puts it
|
|
130
|
+
// above the nine categories and `gtk_flow_box` wraps by DOM order.
|
|
131
|
+
const recentBox = document.createElement('div');
|
|
132
|
+
recentBox.className = 'emoji-flowbox emoji-recent-box';
|
|
133
|
+
recentBox.dataset.section = 'recent';
|
|
134
|
+
this._listEl.appendChild(recentBox);
|
|
135
|
+
this._sections.set('recent', { id: 'recent', heading: null, box: recentBox, button: null, empty: true });
|
|
136
|
+
|
|
137
|
+
this._buildSections();
|
|
138
|
+
this._buildToolbar(root);
|
|
139
|
+
this._renderRecents();
|
|
140
|
+
|
|
141
|
+
// `adj_value_changed` is connected to the scrolled window's adjustment in the C;
|
|
142
|
+
// the scroll event is the DOM's name for the same notification.
|
|
143
|
+
this._listEl.addEventListener('scroll', () => this._markSection(this._currentSection()));
|
|
144
|
+
this.addEventListener('keydown', (event) => (this._ctrl = event.ctrlKey));
|
|
145
|
+
this.addEventListener('keyup', (event) => (this._ctrl = event.ctrlKey));
|
|
146
|
+
// `gtk_emoji_chooser_show` (:1122-1133): the search is CLEARED and the list is back
|
|
147
|
+
// at the top every time the popover appears, which is why re-opening it starts over
|
|
148
|
+
// rather than continuing where the last search left off. The base class's own state
|
|
149
|
+
// change is what says it appeared — `popup()` is not the only way in.
|
|
150
|
+
this.subscribe((open) => {
|
|
151
|
+
if (!open) return;
|
|
152
|
+
this._entry.value = '';
|
|
153
|
+
this._filter();
|
|
154
|
+
this._listEl.scrollTop = 0;
|
|
155
|
+
this._markSection('recent');
|
|
156
|
+
});
|
|
157
|
+
this._markSection('recent');
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/** The emoji picked most recently, newest first — what `recently-used-emoji` holds. */
|
|
161
|
+
get recentEmoji(): EmojiEntry[] {
|
|
162
|
+
return [...this._recent];
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Pick an entry the flow box's `child-activated` would have delivered — which is what
|
|
167
|
+
* a consumer inserting an emoji from elsewhere needs, and what a test drives the recents
|
|
168
|
+
* with. `fromRecent` is `emoji_activated`'s own half of the recent check: an entry
|
|
169
|
+
* picked out of the recent list is not recorded a second time.
|
|
170
|
+
*/
|
|
171
|
+
pick(entry: EmojiEntry, fromRecent = false): void {
|
|
172
|
+
this._activate(entry, null, fromRecent);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// ------------------------------------------------------------- building
|
|
176
|
+
|
|
177
|
+
/** The `.emoji-searchbar` box: the magnifying glyph and the search entry. */
|
|
178
|
+
private _buildSearchbar(): HTMLElement {
|
|
179
|
+
const bar = document.createElement('div');
|
|
180
|
+
bar.className = 'emoji-searchbar';
|
|
181
|
+
|
|
182
|
+
const icon = createGtkImage('edit-find', 'emoji-search-icon');
|
|
183
|
+
|
|
184
|
+
this._entry = document.createElement('input');
|
|
185
|
+
this._entry.type = 'search';
|
|
186
|
+
this._entry.className = 'emoji-search-entry';
|
|
187
|
+
this._entry.placeholder = 'Search emoji';
|
|
188
|
+
this._entry.setAttribute('aria-label', 'Search emoji');
|
|
189
|
+
// `search_changed` — every keystroke re-filters, and an empty search restores all.
|
|
190
|
+
this._entry.addEventListener('input', () => this._filter());
|
|
191
|
+
// `stop_search` is Escape and belongs to the popover; `activate_search` is Enter,
|
|
192
|
+
// and picks the first result of the first non-empty section.
|
|
193
|
+
this._entry.addEventListener('keydown', (event) => {
|
|
194
|
+
if (event.key === 'Enter') {
|
|
195
|
+
event.preventDefault();
|
|
196
|
+
this._activateFirstResult();
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
// `gtk_widget_class_add_binding_action (…, GDK_KEY_n / GDK_KEY_p,
|
|
200
|
+
// GDK_CONTROL_MASK, "scroll.section", "i", ±1)` — the only two shortcuts the
|
|
201
|
+
// widget declares besides Enter.
|
|
202
|
+
if (event.ctrlKey && (event.key === 'n' || event.key === 'p')) {
|
|
203
|
+
event.preventDefault();
|
|
204
|
+
this._scrollSection(event.key === 'n' ? 1 : -1);
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
bar.append(icon, this._entry);
|
|
209
|
+
return bar;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/** The `empty` stack page: the big glyph and the two lines under it. */
|
|
213
|
+
private _buildEmptyPage(): HTMLElement {
|
|
214
|
+
const empty = document.createElement('div');
|
|
215
|
+
empty.className = 'emoji-empty';
|
|
216
|
+
empty.hidden = true;
|
|
217
|
+
empty.appendChild(createGtkImage('edit-find', 'emoji-empty-icon'));
|
|
218
|
+
const title = document.createElement('div');
|
|
219
|
+
title.className = 'emoji-empty-title';
|
|
220
|
+
title.textContent = 'No Results Found';
|
|
221
|
+
const hint = document.createElement('div');
|
|
222
|
+
hint.className = 'emoji-empty-hint';
|
|
223
|
+
hint.textContent = 'Try a different search';
|
|
224
|
+
empty.append(title, hint);
|
|
225
|
+
return empty;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/** One flow box per category, each under its own heading label. */
|
|
229
|
+
private _buildSections(): void {
|
|
230
|
+
for (const category of EMOJI_CATEGORIES) {
|
|
231
|
+
const heading = document.createElement('div');
|
|
232
|
+
heading.className = 'emoji-heading';
|
|
233
|
+
heading.textContent = category.heading;
|
|
234
|
+
|
|
235
|
+
const box = document.createElement('div');
|
|
236
|
+
box.className = 'emoji-flowbox';
|
|
237
|
+
box.dataset.section = category.id;
|
|
238
|
+
for (const entry of category.entries) box.appendChild(this._emojiButton(entry));
|
|
239
|
+
|
|
240
|
+
this._listEl.append(heading, box);
|
|
241
|
+
this._sections.set(category.id, { id: category.id, heading, box, button: null, empty: true });
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* The toolbar: one `.emoji-section` button per section, recent FIRST (the struct
|
|
247
|
+
* order), each with its own symbolic and the `.ui`'s tooltip.
|
|
248
|
+
*
|
|
249
|
+
* The icons are written out one call per section rather than looked up from a table:
|
|
250
|
+
* each name is USED there, which is what makes it a name this stylesheet has to be
|
|
251
|
+
* able to draw — `scripts/check-adwaita-icon-masks.mjs` reads exactly that.
|
|
252
|
+
*/
|
|
253
|
+
private _buildToolbar(root: HTMLElement): void {
|
|
254
|
+
const toolbar = document.createElement('div');
|
|
255
|
+
toolbar.className = 'emoji-toolbar';
|
|
256
|
+
|
|
257
|
+
const buttons: Array<[string, HTMLButtonElement]> = [
|
|
258
|
+
[
|
|
259
|
+
'recent',
|
|
260
|
+
this._sectionButton(
|
|
261
|
+
'recent',
|
|
262
|
+
createGtkImage('emoji-recent', 'emoji-section-icon'),
|
|
263
|
+
emojiTooltip('recent'),
|
|
264
|
+
),
|
|
265
|
+
],
|
|
266
|
+
[
|
|
267
|
+
'people',
|
|
268
|
+
this._sectionButton(
|
|
269
|
+
'people',
|
|
270
|
+
createGtkImage('emoji-people', 'emoji-section-icon'),
|
|
271
|
+
emojiTooltip('people'),
|
|
272
|
+
),
|
|
273
|
+
],
|
|
274
|
+
[
|
|
275
|
+
'body',
|
|
276
|
+
this._sectionButton('body', createGtkImage('emoji-body', 'emoji-section-icon'), emojiTooltip('body')),
|
|
277
|
+
],
|
|
278
|
+
[
|
|
279
|
+
'nature',
|
|
280
|
+
this._sectionButton(
|
|
281
|
+
'nature',
|
|
282
|
+
createGtkImage('emoji-nature', 'emoji-section-icon'),
|
|
283
|
+
emojiTooltip('nature'),
|
|
284
|
+
),
|
|
285
|
+
],
|
|
286
|
+
[
|
|
287
|
+
'food',
|
|
288
|
+
this._sectionButton('food', createGtkImage('emoji-food', 'emoji-section-icon'), emojiTooltip('food')),
|
|
289
|
+
],
|
|
290
|
+
[
|
|
291
|
+
'travel',
|
|
292
|
+
this._sectionButton(
|
|
293
|
+
'travel',
|
|
294
|
+
createGtkImage('emoji-travel', 'emoji-section-icon'),
|
|
295
|
+
emojiTooltip('travel'),
|
|
296
|
+
),
|
|
297
|
+
],
|
|
298
|
+
[
|
|
299
|
+
'activities',
|
|
300
|
+
this._sectionButton(
|
|
301
|
+
'activities',
|
|
302
|
+
createGtkImage('emoji-activities', 'emoji-section-icon'),
|
|
303
|
+
emojiTooltip('activities'),
|
|
304
|
+
),
|
|
305
|
+
],
|
|
306
|
+
[
|
|
307
|
+
'objects',
|
|
308
|
+
this._sectionButton(
|
|
309
|
+
'objects',
|
|
310
|
+
createGtkImage('emoji-objects', 'emoji-section-icon'),
|
|
311
|
+
emojiTooltip('objects'),
|
|
312
|
+
),
|
|
313
|
+
],
|
|
314
|
+
[
|
|
315
|
+
'symbols',
|
|
316
|
+
this._sectionButton(
|
|
317
|
+
'symbols',
|
|
318
|
+
createGtkImage('emoji-symbols', 'emoji-section-icon'),
|
|
319
|
+
emojiTooltip('symbols'),
|
|
320
|
+
),
|
|
321
|
+
],
|
|
322
|
+
[
|
|
323
|
+
'flags',
|
|
324
|
+
this._sectionButton(
|
|
325
|
+
'flags',
|
|
326
|
+
createGtkImage('emoji-flags', 'emoji-section-icon'),
|
|
327
|
+
emojiTooltip('flags'),
|
|
328
|
+
),
|
|
329
|
+
],
|
|
330
|
+
];
|
|
331
|
+
|
|
332
|
+
for (const [id, button] of buttons) {
|
|
333
|
+
button.addEventListener('click', () => this._scrollTo(id));
|
|
334
|
+
const section = this._sections.get(id);
|
|
335
|
+
if (section !== undefined) this._sections.set(id, { ...section, button });
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
toolbar.append(...buttons.map(([, button]) => button));
|
|
339
|
+
root.appendChild(toolbar);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/** `button.image-button.emoji-section` — flat, circular, 32px, with a tooltip. */
|
|
343
|
+
private _sectionButton(id: string, icon: HTMLElement, tooltip: string): HTMLButtonElement {
|
|
344
|
+
const button = document.createElement('button');
|
|
345
|
+
button.type = 'button';
|
|
346
|
+
button.className = 'emoji-section';
|
|
347
|
+
button.dataset.section = id;
|
|
348
|
+
button.title = tooltip;
|
|
349
|
+
button.setAttribute('aria-label', tooltip);
|
|
350
|
+
button.appendChild(icon);
|
|
351
|
+
return button;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* One emoji: a `<button>` holding the glyph at `PANGO_SCALE_X_LARGE`, labelled with
|
|
356
|
+
* the English name — the name the search reads, so it is the accessible name too.
|
|
357
|
+
*/
|
|
358
|
+
private _emojiButton(entry: EmojiEntry): HTMLButtonElement {
|
|
359
|
+
const button = document.createElement('button');
|
|
360
|
+
button.type = 'button';
|
|
361
|
+
button.className = 'emoji';
|
|
362
|
+
button.textContent = entry.text;
|
|
363
|
+
button.title = entry.name;
|
|
364
|
+
button.setAttribute('aria-label', entry.name);
|
|
365
|
+
this._entryOf.set(button, entry);
|
|
366
|
+
// `emoji_activated` (:449) — pick.
|
|
367
|
+
button.addEventListener('click', (event) => {
|
|
368
|
+
this._closeVariations();
|
|
369
|
+
// A second click on an emoji with skin tones opens them, which is what
|
|
370
|
+
// `pressed_cb` (:571) does with the third mouse button.
|
|
371
|
+
if (event.detail > 1 && hasVariations(entry)) {
|
|
372
|
+
this._showVariations(entry, button);
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
this._activate(entry, button);
|
|
376
|
+
});
|
|
377
|
+
return button;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// ------------------------------------------------------------- behaviour
|
|
381
|
+
|
|
382
|
+
/** `filter_func` + `match_tokens`: EVERY term must be a PREFIX of a token of the name
|
|
383
|
+
* or of one of the keywords. `g_str_tokenize_and_fold` case-folds, hence lower-casing
|
|
384
|
+
* both sides. */
|
|
385
|
+
private _matches(entry: EmojiEntry, terms: readonly string[]): boolean {
|
|
386
|
+
if (terms.length === 0) return true;
|
|
387
|
+
const tokens = `${entry.name} ${entry.keywords.join(' ')}`.toLowerCase().split(/\s+/);
|
|
388
|
+
return terms.every((term) => tokens.some((token) => token.startsWith(term)));
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/** `search_changed` — invalidate every section, then `update_headings`. */
|
|
392
|
+
private _filter(): void {
|
|
393
|
+
const terms = this._entry.value
|
|
394
|
+
.toLowerCase()
|
|
395
|
+
.split(/\s+/)
|
|
396
|
+
.filter((term) => term.length > 0);
|
|
397
|
+
for (const section of this._sections.values()) {
|
|
398
|
+
let matched = 0;
|
|
399
|
+
for (const node of section.box.querySelectorAll<HTMLButtonElement>('.emoji')) {
|
|
400
|
+
const entry = this._entryOf.get(node);
|
|
401
|
+
const show = entry !== undefined && this._matches(entry, terms);
|
|
402
|
+
node.hidden = !show;
|
|
403
|
+
if (show) matched += 1;
|
|
404
|
+
}
|
|
405
|
+
// `update_headings` (:967): a section with no match hides BOTH its heading and
|
|
406
|
+
// its emoji, and `empty` is what the section buttons read — an invisible
|
|
407
|
+
// section is not a jumpable one. The recent box is NOT in that function's
|
|
408
|
+
// list: its visibility is `populate_recent_section`'s alone (hidden while
|
|
409
|
+
// the list is empty, shown on the first pick), so filtering never hides it.
|
|
410
|
+
section.empty = matched === 0;
|
|
411
|
+
if (section.heading !== null) section.heading.hidden = section.empty;
|
|
412
|
+
if (section.id !== 'recent') section.box.hidden = section.empty;
|
|
413
|
+
}
|
|
414
|
+
// …and the stack shows `empty` when EVERY section is, `list` otherwise (:984-989).
|
|
415
|
+
const allEmpty = [...this._sections.values()].every((section) => section.empty);
|
|
416
|
+
this._listEl.hidden = allEmpty;
|
|
417
|
+
this._emptyEl.hidden = !allEmpty;
|
|
418
|
+
this._markSection(this._currentSection());
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/** `emoji_activated` — emit, remember, and pop down unless Ctrl is held. */
|
|
422
|
+
private _activate(entry: EmojiEntry, from: HTMLElement | null, fromRecent = false): void {
|
|
423
|
+
if (!fromRecent && from?.parentElement?.dataset.section !== 'recent') this._remember(entry);
|
|
424
|
+
this.dispatchEvent(new CustomEvent('emoji-picked', { bubbles: true, detail: { text: entry.text } }));
|
|
425
|
+
// `should_close` (:433-448): Ctrl held means "keep picking", which is why the
|
|
426
|
+
// popover stays up — and why the entry keeps what was typed into it.
|
|
427
|
+
if (!this._ctrl) this.popdown();
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/** `add_recent_item` (:390) — newest first, no duplicates, at most `MAX_RECENT`. */
|
|
431
|
+
private _remember(entry: EmojiEntry): void {
|
|
432
|
+
this._recent = [entry, ...this._recent.filter((candidate) => candidate.text !== entry.text)].slice(
|
|
433
|
+
0,
|
|
434
|
+
EMOJI_MAX_RECENT,
|
|
435
|
+
);
|
|
436
|
+
this._renderRecents();
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/** `populate_recent_section` (:366-388): the list, its visibility and its button. */
|
|
440
|
+
private _renderRecents(): void {
|
|
441
|
+
const section = this._sections.get('recent');
|
|
442
|
+
if (section === undefined) return;
|
|
443
|
+
section.box.replaceChildren(...this._recent.map((entry) => this._emojiButton(entry)));
|
|
444
|
+
const empty = this._recent.length === 0;
|
|
445
|
+
section.box.hidden = empty;
|
|
446
|
+
section.empty = empty;
|
|
447
|
+
if (section.button !== null) section.button.disabled = empty;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* `show_variations` (:508-548): the plain form and the five skin tones
|
|
452
|
+
* (`add_emoji (box, FALSE, emoji_data, 0, …)` then `1F3FB`…`1F3FF`), in a popover over
|
|
453
|
+
* the emoji — and only for an emoji that HAS them, which is `has_variations` (:489).
|
|
454
|
+
*/
|
|
455
|
+
private _showVariations(entry: EmojiEntry, anchor: HTMLElement): void {
|
|
456
|
+
this._closeVariations();
|
|
457
|
+
const popover = document.createElement('div');
|
|
458
|
+
popover.className = 'emoji-variations';
|
|
459
|
+
const row = document.createElement('div');
|
|
460
|
+
row.className = 'emoji-variations-row';
|
|
461
|
+
const base = [...entry.text].filter((character) => !EMOJI_SKIN_TONES.includes(character)).join('');
|
|
462
|
+
for (const text of [base, ...EMOJI_SKIN_TONES.map((tone) => base + tone)]) {
|
|
463
|
+
const variant = document.createElement('button');
|
|
464
|
+
variant.type = 'button';
|
|
465
|
+
variant.className = 'emoji';
|
|
466
|
+
variant.textContent = text;
|
|
467
|
+
variant.addEventListener('click', (event) => {
|
|
468
|
+
event.stopPropagation();
|
|
469
|
+
this._closeVariations();
|
|
470
|
+
this._activate({ ...entry, text }, variant);
|
|
471
|
+
});
|
|
472
|
+
row.appendChild(variant);
|
|
473
|
+
}
|
|
474
|
+
popover.appendChild(row);
|
|
475
|
+
anchor.insertAdjacentElement('afterend', popover);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
private _closeVariations(): void {
|
|
479
|
+
for (const popover of this.querySelectorAll('.emoji-variations')) popover.remove();
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/** `scroll_to_section` (:312-330) — to the heading, minus `BOX_SPACE`. */
|
|
483
|
+
private _scrollTo(id: string): void {
|
|
484
|
+
const section = this._sections.get(id);
|
|
485
|
+
if (section === undefined || section.empty) return;
|
|
486
|
+
const target = section.heading ?? section.box;
|
|
487
|
+
this._listEl.scrollTo({ top: target.offsetTop - BOX_SPACE, behavior: 'smooth' });
|
|
488
|
+
this._markSection(id);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/** `gtk_emoji_chooser_scroll_section` (:1198) — Ctrl+N / Ctrl+P over the visible ones. */
|
|
492
|
+
private _scrollSection(direction: 1 | -1): void {
|
|
493
|
+
const order = [...this._sections.values()].filter((section) => !section.empty);
|
|
494
|
+
if (order.length === 0) return;
|
|
495
|
+
const at = order.findIndex((section) => section.id === this._currentSection());
|
|
496
|
+
const next = order[Math.min(Math.max((at < 0 ? 0 : at) + direction, 0), order.length - 1)]!;
|
|
497
|
+
this._scrollTo(next.id);
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
/** `activate_search` (:1019) — the first result of the first non-empty section. */
|
|
501
|
+
private _activateFirstResult(): void {
|
|
502
|
+
for (const section of this._sections.values()) {
|
|
503
|
+
if (section.empty) continue;
|
|
504
|
+
const first = [...section.box.querySelectorAll<HTMLButtonElement>('.emoji')].find((node) => !node.hidden);
|
|
505
|
+
if (first === undefined) continue;
|
|
506
|
+
this._activate(
|
|
507
|
+
this._entryOf.get(first) ?? { text: first.textContent ?? '', name: '', keywords: [] },
|
|
508
|
+
first,
|
|
509
|
+
);
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* `adj_value_changed` (:814-870): the section the scroll position is INSIDE is the
|
|
516
|
+
* checked one, so the toolbar follows the list without any interaction of its own.
|
|
517
|
+
*/
|
|
518
|
+
private _sectionAt(offset: number): string {
|
|
519
|
+
let selected = 'recent';
|
|
520
|
+
for (const section of this._sections.values()) {
|
|
521
|
+
if (section.empty || section.box.hidden) continue;
|
|
522
|
+
const target = section.heading ?? section.box;
|
|
523
|
+
if (offset < target.offsetTop - BOX_SPACE) break;
|
|
524
|
+
selected = section.id;
|
|
525
|
+
}
|
|
526
|
+
return selected;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
private _currentSection(): string {
|
|
530
|
+
return this._sectionAt(this._listEl.scrollTop);
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
private _markSection(id: string): void {
|
|
534
|
+
for (const section of this._sections.values()) {
|
|
535
|
+
// `:checked` is a state a `<button>` cannot raise, so the class stands in for
|
|
536
|
+
// it — the same spelling `<gtk-check-button>` uses for the same reason.
|
|
537
|
+
section.button?.classList.toggle('checked', section.id === id);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
customElements.define('gtk-emoji-chooser', GtkEmojiChooser);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// <gtk-entry> — Adwaita single-line text entry (e.g. a browser URL bar).
|
|
2
|
-
// Attributes: value, placeholder
|
|
2
|
+
// Attributes: value, placeholder-text (`placeholder` is the older spelling and still read),
|
|
3
|
+
// width-request, type, disabled, maxlength.
|
|
3
4
|
// Properties: value (get/set, proxies the inner input), maxLength, textLength.
|
|
4
5
|
// Events: native `input` bubbles from the inner input; `activate` (CustomEvent)
|
|
5
6
|
// fires on Enter — mirroring Gtk.Entry's `activate` signal.
|
|
@@ -22,12 +23,18 @@
|
|
|
22
23
|
import { ENTRY_ROW_MAX_LENGTH_LIMIT, clampEntryText, entryTextLength } from '@gjsify/adwaita-core';
|
|
23
24
|
|
|
24
25
|
export class GtkEntry extends HTMLElement {
|
|
25
|
-
|
|
26
|
+
/**
|
|
27
|
+
* `protected`, not `private`: `GtkPasswordEntry` and `GtkSearchEntry` subclass this
|
|
28
|
+
* element and restyle or wrap the very input it builds (the password field's Caps Lock
|
|
29
|
+
* and peek listeners, the search field's leading icon and trailing clear button), so the
|
|
30
|
+
* inner node is theirs to reach as well as this class's.
|
|
31
|
+
*/
|
|
32
|
+
protected _input!: HTMLInputElement;
|
|
26
33
|
private _initialized = false;
|
|
27
34
|
private _maxLength = 0;
|
|
28
35
|
|
|
29
36
|
static get observedAttributes() {
|
|
30
|
-
return ['value', 'placeholder', 'type', 'disabled', 'maxlength'];
|
|
37
|
+
return ['value', 'placeholder-text', 'placeholder', 'width-request', 'type', 'disabled', 'maxlength'];
|
|
31
38
|
}
|
|
32
39
|
|
|
33
40
|
get value(): string {
|
|
@@ -57,6 +64,20 @@ export class GtkEntry extends HTMLElement {
|
|
|
57
64
|
return entryTextLength(this.value);
|
|
58
65
|
}
|
|
59
66
|
|
|
67
|
+
/**
|
|
68
|
+
* `Gtk.Entry:placeholder-text`, the name a Blueprint file and the gallery trees use.
|
|
69
|
+
* `placeholder` stays for markup written before the GTK spelling was read.
|
|
70
|
+
*/
|
|
71
|
+
private placeholderAttribute(): string {
|
|
72
|
+
return this.getAttribute('placeholder-text') ?? this.getAttribute('placeholder') ?? '';
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** `Gtk.Widget:width-request` — a minimum width in px; -1 or absent leaves the natural one. */
|
|
76
|
+
private applyWidthRequest(): void {
|
|
77
|
+
const width = Number(this.getAttribute('width-request') ?? -1);
|
|
78
|
+
this.style.minWidth = Number.isFinite(width) && width > 0 ? `${width}px` : '';
|
|
79
|
+
}
|
|
80
|
+
|
|
60
81
|
/** The inner native input (for focus/selection). */
|
|
61
82
|
get input(): HTMLInputElement {
|
|
62
83
|
return this._input;
|
|
@@ -72,7 +93,8 @@ export class GtkEntry extends HTMLElement {
|
|
|
72
93
|
input.className = 'adw-entry';
|
|
73
94
|
input.type = this.getAttribute('type') || 'text';
|
|
74
95
|
input.value = clampEntryText(this.getAttribute('value') ?? '', this._maxLength);
|
|
75
|
-
input.placeholder = this.
|
|
96
|
+
input.placeholder = this.placeholderAttribute();
|
|
97
|
+
this.applyWidthRequest();
|
|
76
98
|
input.disabled = this.hasAttribute('disabled');
|
|
77
99
|
// Typing past the limit is clamped here, on the way in — the same place
|
|
78
100
|
// NativeScript clamps it, so both renderers refuse the same character.
|
|
@@ -90,14 +112,26 @@ export class GtkEntry extends HTMLElement {
|
|
|
90
112
|
this.replaceChildren(input);
|
|
91
113
|
}
|
|
92
114
|
|
|
115
|
+
/**
|
|
116
|
+
* Declared so a subclass can override it and still reach the base: the custom-element
|
|
117
|
+
* lifecycle hooks a subclass chains (`super.disconnectedCallback()`) have to exist on
|
|
118
|
+
* the class it extends, and `GtkSearchEntry` drops its pending search timeout here.
|
|
119
|
+
*/
|
|
120
|
+
disconnectedCallback() {}
|
|
121
|
+
|
|
93
122
|
attributeChangedCallback(name: string, _old: string | null, value: string | null) {
|
|
94
123
|
if (name === 'maxlength') {
|
|
95
124
|
this.maxLength = Number(value ?? 0);
|
|
96
125
|
return;
|
|
97
126
|
}
|
|
127
|
+
if (name === 'width-request') {
|
|
128
|
+
this.applyWidthRequest();
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
98
131
|
if (!this._input) return;
|
|
99
132
|
if (name === 'value') this._input.value = clampEntryText(value ?? '', this._maxLength);
|
|
100
|
-
else if (name === 'placeholder'
|
|
133
|
+
else if (name === 'placeholder-text' || name === 'placeholder')
|
|
134
|
+
this._input.placeholder = this.placeholderAttribute();
|
|
101
135
|
else if (name === 'type') this._input.type = value || 'text';
|
|
102
136
|
else if (name === 'disabled') this._input.disabled = value !== null;
|
|
103
137
|
}
|