@gjsify/adwaita-web 0.30.0 → 0.32.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 +786 -272
- package/dist/adwaita-web.css.map +1 -1
- package/lib/types/adw-about-dialog.spec.d.ts +1 -0
- package/lib/types/adw-action-rows.spec.d.ts +1 -0
- package/lib/types/adw-avatar.spec.d.ts +1 -0
- package/lib/types/adw-banner.spec.d.ts +1 -0
- package/lib/types/adw-button-content.spec.d.ts +1 -0
- package/lib/types/adw-carousel.spec.d.ts +1 -0
- package/lib/types/adw-checks.spec.d.ts +1 -0
- package/lib/types/adw-header-bar.spec.d.ts +1 -0
- package/lib/types/adw-icon.spec.d.ts +1 -0
- package/lib/types/adw-progress-bar.spec.d.ts +1 -0
- package/lib/types/adw-sidebar.spec.d.ts +1 -0
- package/lib/types/adw-switch.spec.d.ts +1 -0
- package/lib/types/adw-wrap-box.spec.d.ts +1 -0
- package/lib/types/bottom-sheet.spec.d.ts +1 -0
- package/lib/types/breakpoints.d.ts +22 -0
- package/lib/types/breakpoints.spec.d.ts +1 -0
- package/lib/types/chrome.spec.d.ts +1 -0
- package/lib/types/elements/adw-about-dialog.d.ts +47 -8
- package/lib/types/elements/adw-action-row.d.ts +35 -0
- package/lib/types/elements/adw-avatar.d.ts +11 -0
- package/lib/types/elements/adw-bottom-sheet.d.ts +33 -4
- package/lib/types/elements/adw-button-content.d.ts +12 -0
- package/lib/types/elements/adw-button-row.d.ts +10 -1
- package/lib/types/elements/adw-carousel.d.ts +111 -8
- package/lib/types/elements/adw-checks.d.ts +57 -0
- package/lib/types/elements/adw-clamp.d.ts +4 -1
- package/lib/types/elements/adw-data-grid.d.ts +11 -31
- package/lib/types/elements/adw-drop-down.d.ts +17 -20
- package/lib/types/elements/adw-entry-row.d.ts +93 -3
- package/lib/types/elements/adw-expander-row.d.ts +7 -1
- package/lib/types/elements/adw-header-bar.d.ts +15 -0
- package/lib/types/elements/adw-icon.d.ts +25 -0
- package/lib/types/elements/adw-inline-view-switcher.d.ts +37 -11
- package/lib/types/elements/adw-menu-button.d.ts +6 -6
- package/lib/types/elements/adw-navigation-split-view.d.ts +6 -0
- package/lib/types/elements/adw-navigation-view.d.ts +63 -20
- package/lib/types/elements/adw-overlay-split-view.d.ts +38 -0
- package/lib/types/elements/adw-password-entry-row.d.ts +19 -10
- package/lib/types/elements/adw-popover.d.ts +58 -0
- package/lib/types/elements/adw-preferences-dialog.d.ts +57 -2
- package/lib/types/elements/adw-preferences-group.d.ts +31 -0
- package/lib/types/elements/adw-progress-bar.d.ts +38 -0
- package/lib/types/elements/adw-sidebar.d.ts +37 -5
- package/lib/types/elements/adw-split-button.d.ts +53 -7
- package/lib/types/elements/adw-switch-row.d.ts +11 -2
- package/lib/types/elements/adw-switch.d.ts +15 -0
- package/lib/types/elements/adw-tab-view.d.ts +129 -12
- package/lib/types/elements/adw-toolbar-view.d.ts +5 -0
- package/lib/types/elements/adw-view-stack.d.ts +27 -19
- package/lib/types/elements/adw-view-switcher-bar.d.ts +23 -7
- package/lib/types/elements/adw-view-switcher.d.ts +22 -11
- package/lib/types/elements/adw-window-title.d.ts +3 -1
- package/lib/types/elements/view-switcher-dom.d.ts +44 -0
- package/lib/types/entry-rows.spec.d.ts +1 -0
- package/lib/types/index.d.ts +7 -0
- package/lib/types/navigation-view.spec.d.ts +1 -0
- package/lib/types/preferences-search.d.ts +20 -0
- package/lib/types/preferences.spec.d.ts +1 -0
- package/lib/types/split-button.spec.d.ts +1 -0
- package/lib/types/split-views.spec.d.ts +1 -0
- package/lib/types/styles.generated.d.ts +1 -1
- package/lib/types/tab-view.spec.d.ts +1 -0
- package/lib/types/view-stack.spec.d.ts +1 -0
- package/lib/types/view-switcher.spec.d.ts +1 -0
- package/package.json +6 -6
- package/scss/_banner.scss +13 -0
- package/scss/_bottom_sheet.scss +5 -6
- package/scss/_button.scss +3 -26
- package/scss/_button_content.scss +28 -0
- package/scss/_checks.scss +228 -0
- package/scss/_clamp.scss +9 -3
- package/scss/_data_grid.scss +15 -7
- package/scss/_drop_down.scss +12 -55
- package/scss/_expander_row.scss +5 -50
- package/scss/_icon.scss +51 -0
- package/scss/_icons.generated.scss +6 -0
- package/scss/_inline_view_switcher.scss +34 -0
- package/scss/_labels.scss +135 -0
- package/scss/_menu_button.scss +10 -70
- package/scss/_popover.scss +186 -0
- package/scss/_preferences.scss +29 -0
- package/scss/_preferences_dialog.scss +17 -0
- package/scss/_progress_bar.scss +161 -0
- package/scss/_reset.scss +18 -7
- package/scss/_spinner.scss +7 -3
- package/scss/_split_button.scss +6 -50
- package/scss/_switch.scss +90 -0
- package/scss/_switch_row.scss +6 -52
- package/scss/_tab_view.scss +64 -0
- package/scss/_theme.scss +10 -0
- package/scss/_toolbar_view.scss +74 -0
- package/scss/_variables.scss +34 -0
- package/scss/_view_switcher.scss +40 -0
- package/scss/_view_switcher_bar.scss +37 -0
- package/scss/_wrap_box.scss +56 -6
- package/scss/adwaita-skin.scss +23 -0
- package/src/adw-about-dialog.spec.ts +350 -0
- package/src/adw-action-rows.spec.ts +342 -0
- package/src/adw-avatar.spec.ts +139 -0
- package/src/adw-banner.spec.ts +152 -0
- package/src/adw-button-content.spec.ts +183 -0
- package/src/adw-button-row.spec.ts +12 -6
- package/src/adw-carousel.spec.ts +389 -0
- package/src/adw-checks.spec.ts +368 -0
- package/src/adw-drop-down.spec.ts +123 -0
- package/src/adw-header-bar.spec.ts +83 -0
- package/src/adw-icon.spec.ts +167 -0
- package/src/adw-progress-bar.spec.ts +173 -0
- package/src/adw-sidebar.spec.ts +332 -0
- package/src/adw-switch.spec.ts +242 -0
- package/src/adw-tab-view.spec.ts +12 -3
- package/src/adw-view-switcher-bar.spec.ts +61 -13
- package/src/adw-wrap-box.spec.ts +194 -0
- package/src/bottom-sheet.spec.ts +325 -0
- package/src/breakpoints.spec.ts +147 -0
- package/src/breakpoints.ts +67 -0
- package/src/chrome.spec.ts +301 -0
- package/src/elements/adw-about-dialog.ts +223 -163
- package/src/elements/adw-action-row.ts +120 -10
- package/src/elements/adw-avatar.ts +48 -54
- package/src/elements/adw-banner.ts +46 -10
- package/src/elements/adw-bottom-sheet.ts +145 -60
- package/src/elements/adw-button-content.ts +107 -33
- package/src/elements/adw-button-row.ts +61 -37
- package/src/elements/adw-button.ts +5 -7
- package/src/elements/adw-carousel.ts +467 -105
- package/src/elements/adw-checks.ts +299 -0
- package/src/elements/adw-clamp.ts +80 -10
- package/src/elements/adw-combo-row.ts +15 -7
- package/src/elements/adw-data-grid.ts +43 -91
- package/src/elements/adw-drop-down.ts +117 -128
- package/src/elements/adw-entry-row.ts +404 -36
- package/src/elements/adw-expander-row.ts +61 -29
- package/src/elements/adw-header-bar.ts +31 -6
- package/src/elements/adw-icon.ts +148 -0
- package/src/elements/adw-inline-view-switcher.ts +240 -129
- package/src/elements/adw-menu-button.ts +93 -70
- package/src/elements/adw-navigation-split-view.ts +48 -7
- package/src/elements/adw-navigation-view.ts +286 -148
- package/src/elements/adw-overlay-split-view.ts +164 -14
- package/src/elements/adw-password-entry-row.ts +111 -89
- package/src/elements/adw-popover.ts +283 -0
- package/src/elements/adw-preferences-dialog.ts +120 -21
- package/src/elements/adw-preferences-group.ts +128 -22
- package/src/elements/adw-progress-bar.ts +189 -0
- package/src/elements/adw-sidebar.ts +301 -142
- package/src/elements/adw-spin-row.ts +11 -5
- package/src/elements/adw-spinner.ts +23 -8
- package/src/elements/adw-split-button.ts +371 -92
- package/src/elements/adw-status-page.ts +9 -9
- package/src/elements/adw-switch-row.ts +100 -30
- package/src/elements/adw-switch.ts +129 -0
- package/src/elements/adw-tab-view.ts +682 -129
- package/src/elements/adw-toggle-group.ts +5 -7
- package/src/elements/adw-toolbar-view.ts +84 -21
- package/src/elements/adw-view-stack.ts +162 -85
- package/src/elements/adw-view-switcher-bar.ts +163 -81
- package/src/elements/adw-view-switcher.ts +231 -112
- package/src/elements/adw-window-title.ts +44 -11
- package/src/elements/adw-wrap-box.ts +131 -35
- package/src/elements/view-switcher-dom.ts +108 -0
- package/src/entry-rows.spec.ts +391 -0
- package/src/index.ts +23 -0
- package/src/navigation-view.spec.ts +435 -0
- package/src/preferences-search.ts +135 -0
- package/src/preferences.spec.ts +250 -0
- package/src/source-view/adw-source-view.ts +2 -5
- package/src/split-button.spec.ts +470 -0
- package/src/split-views.spec.ts +309 -0
- package/src/styles.generated.ts +1 -1
- package/src/tab-view.spec.ts +451 -0
- package/src/test.browser.mts +49 -0
- package/src/view-stack.spec.ts +272 -0
- package/src/view-switcher.spec.ts +446 -0
|
@@ -5,6 +5,20 @@
|
|
|
5
5
|
// layer covers the content and dismisses the sheet on click (unless `can-close`
|
|
6
6
|
// is false, in which case a close attempt is signalled instead).
|
|
7
7
|
//
|
|
8
|
+
// The open/closed state and the dismissal gate are NOT implemented here: they
|
|
9
|
+
// live in `@gjsify/adwaita-core` as `BottomSheetPresentation` +
|
|
10
|
+
// `resolveBottomSheetClose`, ported from the C source and shared with
|
|
11
|
+
// `@gjsify/adwaita-nativescript` (ADR 0004). This element owns only the DOM.
|
|
12
|
+
// Three things changed when it moved:
|
|
13
|
+
// - `open="false"` now means CLOSED. The old getter was `hasAttribute('open')`
|
|
14
|
+
// while every other boolean used `!== 'false'`, so one element carried two
|
|
15
|
+
// incompatible conventions and `<adw-bottom-sheet open="false">` rendered
|
|
16
|
+
// open (adw-bottom-sheet.c:1670 — `open = !!open` on a strict gboolean).
|
|
17
|
+
// - `notify::open` follows the STATE, not the attribute VALUE. Rewriting the
|
|
18
|
+
// attribute to another truthy spelling no longer fires a notification
|
|
19
|
+
// carrying an unchanged payload (adw-bottom-sheet.c:1672-1682).
|
|
20
|
+
// - the drag handle is decorative, not a close button — see below.
|
|
21
|
+
//
|
|
8
22
|
// Children are declared as <adw-bottom-sheet-content> (the persistent content)
|
|
9
23
|
// and <adw-bottom-sheet-sheet> (the slide-up sheet); both are consumed at
|
|
10
24
|
// connect time. Unslotted children fall back to the content, mirroring the
|
|
@@ -13,22 +27,30 @@
|
|
|
13
27
|
// Attributes:
|
|
14
28
|
// open (boolean — whether the sheet is revealed; default closed)
|
|
15
29
|
// modal (boolean — dim + block the content while open; default on)
|
|
16
|
-
// can-close (boolean — user can dismiss via the
|
|
17
|
-
// default on. When off, a dismissal
|
|
18
|
-
// instead of closing — mirrors
|
|
30
|
+
// can-close (boolean — user can dismiss via the dimming / Esc / the
|
|
31
|
+
// sheet.close action; default on. When off, a dismissal
|
|
32
|
+
// raises `close-attempt` instead of closing — mirrors
|
|
33
|
+
// Adw.BottomSheet:can-close)
|
|
19
34
|
// show-drag-handle (boolean — overlay the drag handle on the sheet; default on)
|
|
20
35
|
//
|
|
21
36
|
// Events:
|
|
22
37
|
// `notify::open` (CustomEvent, bubbles, `detail = { open }`) when the revealed
|
|
23
38
|
// state changes — mirrors the Adw.BottomSheet `open` GObject property.
|
|
24
|
-
// `close-attempt` (CustomEvent, bubbles) when a dismissal is attempted
|
|
25
|
-
//
|
|
39
|
+
// `close-attempt` (CustomEvent, bubbles) when a dismissal is attempted that the
|
|
40
|
+
// sheet refuses — mirrors the Adw.BottomSheet `close-attempt` signal.
|
|
41
|
+
// `sheet.close` (CustomEvent, bubbles) when the sheet.close action is used on
|
|
42
|
+
// an ALREADY-closed sheet, so an enclosing sheet/dialog can handle it — the
|
|
43
|
+
// DOM equivalent of `gtk_widget_activate_action (parent, "sheet.close")`
|
|
44
|
+
// (adw-bottom-sheet.c:382-385).
|
|
26
45
|
//
|
|
27
46
|
// Reference: refs/libadwaita/src/adw-bottom-sheet.c (AdwBottomSheet behaviour)
|
|
28
47
|
// Reference: refs/libadwaita/src/stylesheet/widgets/_bottom-sheet.scss
|
|
29
48
|
// Copyright (c) 2023-2024 GNOME Foundation Inc. (libadwaita). LGPLv2.1+.
|
|
30
49
|
// Modifications: Implemented as a Web Component for @gjsify/adwaita-web.
|
|
31
50
|
|
|
51
|
+
import { BottomSheetPresentation } from '@gjsify/adwaita-core';
|
|
52
|
+
import type { BottomSheetCloseOutcome, BottomSheetCloseSource } from '@gjsify/adwaita-core';
|
|
53
|
+
|
|
32
54
|
/** The persistent content. Child of <adw-bottom-sheet>; consumed at connect time. */
|
|
33
55
|
export class AdwBottomSheetContent extends HTMLElement {}
|
|
34
56
|
|
|
@@ -41,6 +63,10 @@ export class AdwBottomSheet extends HTMLElement {
|
|
|
41
63
|
private _dimmingEl!: HTMLDivElement;
|
|
42
64
|
private _sheetEl!: HTMLDivElement;
|
|
43
65
|
private _dragHandleEl!: HTMLDivElement;
|
|
66
|
+
/** The shared open/can-close model — the single source of truth for both. */
|
|
67
|
+
private readonly _state = new BottomSheetPresentation();
|
|
68
|
+
/** Set while the element writes `open` back, so the write does not re-enter. */
|
|
69
|
+
private _reflecting = false;
|
|
44
70
|
|
|
45
71
|
static get observedAttributes() {
|
|
46
72
|
return ['open', 'modal', 'can-close', 'show-drag-handle'];
|
|
@@ -48,26 +74,25 @@ export class AdwBottomSheet extends HTMLElement {
|
|
|
48
74
|
|
|
49
75
|
/** Whether the sheet is revealed. */
|
|
50
76
|
get open(): boolean {
|
|
51
|
-
return this.
|
|
77
|
+
return this._state.open;
|
|
52
78
|
}
|
|
53
79
|
|
|
54
80
|
set open(value: boolean) {
|
|
55
|
-
|
|
56
|
-
else this.removeAttribute('open');
|
|
81
|
+
this._setOpen(!!value);
|
|
57
82
|
}
|
|
58
83
|
|
|
59
84
|
/** Whether the content is dimmed + blocked while the sheet is open. */
|
|
60
85
|
get modal(): boolean {
|
|
61
|
-
return this._boolAttr('modal');
|
|
86
|
+
return this._boolAttr('modal', true);
|
|
62
87
|
}
|
|
63
88
|
|
|
64
89
|
set modal(value: boolean) {
|
|
65
90
|
this._setBoolAttr('modal', value);
|
|
66
91
|
}
|
|
67
92
|
|
|
68
|
-
/** Whether the user can dismiss the sheet (
|
|
93
|
+
/** Whether the user can dismiss the sheet (dimming / Escape / sheet.close). */
|
|
69
94
|
get canClose(): boolean {
|
|
70
|
-
return this.
|
|
95
|
+
return this._state.canClose;
|
|
71
96
|
}
|
|
72
97
|
|
|
73
98
|
set canClose(value: boolean) {
|
|
@@ -76,13 +101,33 @@ export class AdwBottomSheet extends HTMLElement {
|
|
|
76
101
|
|
|
77
102
|
/** Whether the overlaid drag handle is shown. */
|
|
78
103
|
get showDragHandle(): boolean {
|
|
79
|
-
return this._boolAttr('show-drag-handle');
|
|
104
|
+
return this._boolAttr('show-drag-handle', true);
|
|
80
105
|
}
|
|
81
106
|
|
|
82
107
|
set showDragHandle(value: boolean) {
|
|
83
108
|
this._setBoolAttr('show-drag-handle', value);
|
|
84
109
|
}
|
|
85
110
|
|
|
111
|
+
/**
|
|
112
|
+
* Route a dismissal affordance through the shared gate and act on the
|
|
113
|
+
* verdict: close, raise `close-attempt`, forward `sheet.close` to an
|
|
114
|
+
* ancestor, or do nothing. The element calls this for the dimming layer and
|
|
115
|
+
* Escape; a close button inside the sheet calls it with `'close-button'`.
|
|
116
|
+
*
|
|
117
|
+
* The verdict differs per source — a locked sheet signals a scrim click but
|
|
118
|
+
* swallows a swipe, and the drag handle never closes anything. See
|
|
119
|
+
* `resolveBottomSheetClose` in `@gjsify/adwaita-core`.
|
|
120
|
+
*/
|
|
121
|
+
requestClose(source: BottomSheetCloseSource): BottomSheetCloseOutcome {
|
|
122
|
+
const outcome = this._state.requestClose(source);
|
|
123
|
+
if (outcome === 'close-attempt') {
|
|
124
|
+
this.dispatchEvent(new CustomEvent('close-attempt', { bubbles: true }));
|
|
125
|
+
} else if (outcome === 'delegate') {
|
|
126
|
+
this.dispatchEvent(new CustomEvent('sheet.close', { bubbles: true }));
|
|
127
|
+
}
|
|
128
|
+
return outcome;
|
|
129
|
+
}
|
|
130
|
+
|
|
86
131
|
connectedCallback() {
|
|
87
132
|
if (this._initialized) return;
|
|
88
133
|
this._initialized = true;
|
|
@@ -90,9 +135,9 @@ export class AdwBottomSheet extends HTMLElement {
|
|
|
90
135
|
// Snapshot declared children before taking over the subtree. The sheet
|
|
91
136
|
// and content are placed by slot; anything unslotted falls back to the
|
|
92
137
|
// content (the AdwBottomSheet GtkBuildable default).
|
|
93
|
-
const
|
|
94
|
-
const
|
|
95
|
-
const
|
|
138
|
+
const claimed = new Set<Node>();
|
|
139
|
+
const sheetChildren = this._collectSlot('adw-bottom-sheet-sheet', 'sheet', claimed);
|
|
140
|
+
const contentChildren = this._collectSlot('adw-bottom-sheet-content', 'content', claimed);
|
|
96
141
|
const unslotted = Array.from(this.childNodes).filter((n) => !claimed.has(n));
|
|
97
142
|
|
|
98
143
|
// Persistent content layer.
|
|
@@ -105,25 +150,22 @@ export class AdwBottomSheet extends HTMLElement {
|
|
|
105
150
|
// Clicking it dismisses the sheet (or raises close-attempt when locked).
|
|
106
151
|
this._dimmingEl = document.createElement('div');
|
|
107
152
|
this._dimmingEl.className = 'adw-bottom-sheet-dimming';
|
|
108
|
-
this._dimmingEl.addEventListener('click', () => this.
|
|
153
|
+
this._dimmingEl.addEventListener('click', () => this.requestClose('dimming'));
|
|
109
154
|
|
|
110
155
|
// The slide-up sheet with its overlaid drag handle.
|
|
111
156
|
this._sheetEl = document.createElement('div');
|
|
112
157
|
this._sheetEl.className = 'adw-bottom-sheet-sheet';
|
|
113
158
|
|
|
159
|
+
// libadwaita builds the handle with can_focus = FALSE and
|
|
160
|
+
// can_target = FALSE (adw-bottom-sheet.c:1197-1198): it is a decorative
|
|
161
|
+
// pill that cannot be clicked or focused at all. Its only behavioural
|
|
162
|
+
// role is elsewhere — `allow_mouse_drag = show_drag_handle || bottom_bar`
|
|
163
|
+
// (adw-bottom-sheet.c:455-457). This element used to give it
|
|
164
|
+
// role="button", tabindex=0 and a click-to-close handler, which is the
|
|
165
|
+
// one behaviour it provably does not have.
|
|
114
166
|
this._dragHandleEl = document.createElement('div');
|
|
115
167
|
this._dragHandleEl.className = 'adw-bottom-sheet-drag-handle';
|
|
116
|
-
this._dragHandleEl.setAttribute('
|
|
117
|
-
this._dragHandleEl.setAttribute('aria-label', 'Drag handle');
|
|
118
|
-
this._dragHandleEl.tabIndex = 0;
|
|
119
|
-
// The handle is the primary pointer affordance for closing the sheet.
|
|
120
|
-
this._dragHandleEl.addEventListener('click', () => this._attemptClose());
|
|
121
|
-
this._dragHandleEl.addEventListener('keydown', (event) => {
|
|
122
|
-
if (event.key === 'Enter' || event.key === ' ') {
|
|
123
|
-
event.preventDefault();
|
|
124
|
-
this._attemptClose();
|
|
125
|
-
}
|
|
126
|
-
});
|
|
168
|
+
this._dragHandleEl.setAttribute('aria-hidden', 'true');
|
|
127
169
|
|
|
128
170
|
const sheetBody = document.createElement('div');
|
|
129
171
|
sheetBody.className = 'adw-bottom-sheet-sheet-body';
|
|
@@ -133,56 +175,83 @@ export class AdwBottomSheet extends HTMLElement {
|
|
|
133
175
|
|
|
134
176
|
this.replaceChildren(this._contentEl, this._dimmingEl, this._sheetEl);
|
|
135
177
|
|
|
136
|
-
// Escape
|
|
137
|
-
// shortcut
|
|
178
|
+
// Escape → maybe_close_cb (adw-bottom-sheet.c:389-400). In GTK the
|
|
179
|
+
// shortcut sits on the sheet itself, so it fires exactly while the sheet
|
|
180
|
+
// has focus — INCLUDING while the sheet is closed, which is the corner
|
|
181
|
+
// that still emits `close-attempt`. This port has no focus transfer into
|
|
182
|
+
// the sheet, so an open sheet counts as the focused surface and a closed
|
|
183
|
+
// one only when focus is still inside its (off-screen) body.
|
|
138
184
|
this.addEventListener('keydown', (event) => {
|
|
139
|
-
if (event.key
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
185
|
+
if (event.key !== 'Escape') return;
|
|
186
|
+
if (!this.open && !this._sheetEl.contains(event.target as Node)) return;
|
|
187
|
+
event.stopPropagation();
|
|
188
|
+
this.requestClose('escape');
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
// Adopt whatever the author declared, then start mirroring state changes
|
|
192
|
+
// back out. Seeding before the subscription keeps the initial value
|
|
193
|
+
// silent, as an attribute set during upgrade has always been.
|
|
194
|
+
this._state.setCanClose(this._boolAttr('can-close', true));
|
|
195
|
+
this._state.setOpen(this._boolAttr('open', false));
|
|
196
|
+
this._state.subscribe((open) => {
|
|
197
|
+
this._render();
|
|
198
|
+
this.dispatchEvent(new CustomEvent('notify::open', { bubbles: true, detail: { open } }));
|
|
143
199
|
});
|
|
144
200
|
|
|
145
201
|
this._render();
|
|
146
202
|
}
|
|
147
203
|
|
|
148
204
|
attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null) {
|
|
149
|
-
|
|
205
|
+
// Our own write-back of `open`; the state already holds this value.
|
|
206
|
+
if (this._reflecting) return;
|
|
150
207
|
if (oldValue === newValue) return;
|
|
151
|
-
|
|
208
|
+
|
|
209
|
+
// The state is authoritative even before the DOM exists, so an attribute
|
|
210
|
+
// set during upgrade is not lost. Only the rendering half waits.
|
|
152
211
|
if (name === 'open') {
|
|
153
|
-
|
|
212
|
+
// A no-op change (one truthy spelling for another) leaves the state
|
|
213
|
+
// alone, so nothing renders and nothing notifies.
|
|
214
|
+
this._state.setOpen(this._boolAttr('open', false));
|
|
215
|
+
return;
|
|
154
216
|
}
|
|
217
|
+
if (name === 'can-close') this._state.setCanClose(this._boolAttr('can-close', true));
|
|
218
|
+
if (this._initialized) this._render();
|
|
155
219
|
}
|
|
156
220
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
if (!this.
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
// (it no-ops on an unchanged value, but the notify::open fires from the
|
|
165
|
-
// attribute write below).
|
|
166
|
-
this.open = false;
|
|
221
|
+
/** The programmatic open/close — `AdwBottomSheet:open`, ungated by `can-close`. */
|
|
222
|
+
private _setOpen(open: boolean): void {
|
|
223
|
+
if (!this._state.setOpen(open)) return;
|
|
224
|
+
// Once connected the subscription renders (which reflects the attribute)
|
|
225
|
+
// and notifies; before that there is no DOM, so only the attribute has
|
|
226
|
+
// to be kept in step.
|
|
227
|
+
if (!this._initialized) this._reflectOpen(open);
|
|
167
228
|
}
|
|
168
229
|
|
|
169
230
|
private _render(): void {
|
|
170
|
-
|
|
231
|
+
const open = this._state.open;
|
|
232
|
+
this._reflectOpen(open);
|
|
233
|
+
this.classList.toggle('open', open);
|
|
171
234
|
this.classList.toggle('modal', this.modal);
|
|
172
235
|
this.classList.toggle('has-drag-handle', this.showDragHandle);
|
|
173
236
|
|
|
174
237
|
// The dimming layer is only meaningful while modal; it fades in with the
|
|
175
238
|
// sheet and is non-interactive when closed.
|
|
176
|
-
this._dimmingEl.classList.toggle('visible',
|
|
239
|
+
this._dimmingEl.classList.toggle('visible', open && this.modal);
|
|
177
240
|
this._dragHandleEl.hidden = !this.showDragHandle;
|
|
178
241
|
}
|
|
179
242
|
|
|
180
|
-
/**
|
|
181
|
-
private
|
|
243
|
+
/** Mirror the state onto the `open` attribute (the CSS + markup hook). */
|
|
244
|
+
private _reflectOpen(open: boolean): void {
|
|
245
|
+
this._reflecting = true;
|
|
246
|
+
if (open) this.setAttribute('open', '');
|
|
247
|
+
else this.removeAttribute('open');
|
|
248
|
+
this._reflecting = false;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/** Read a boolean attribute: absent → `fallback`, explicit `="false"` → off. */
|
|
252
|
+
private _boolAttr(name: string, fallback: boolean): boolean {
|
|
182
253
|
const value = this.getAttribute(name);
|
|
183
|
-
|
|
184
|
-
if (value === null) return name === 'modal' || name === 'can-close' || name === 'show-drag-handle';
|
|
185
|
-
return value !== 'false';
|
|
254
|
+
return value === null ? fallback : value !== 'false';
|
|
186
255
|
}
|
|
187
256
|
|
|
188
257
|
private _setBoolAttr(name: string, value: boolean): void {
|
|
@@ -190,13 +259,29 @@ export class AdwBottomSheet extends HTMLElement {
|
|
|
190
259
|
else this.setAttribute(name, 'false');
|
|
191
260
|
}
|
|
192
261
|
|
|
193
|
-
/**
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
262
|
+
/**
|
|
263
|
+
* Collect the nodes declared for one slot, in DOCUMENT order, marking every
|
|
264
|
+
* consumed child in `claimed`.
|
|
265
|
+
*
|
|
266
|
+
* The `<adw-bottom-sheet-sheet>` / `<adw-bottom-sheet-content>` wrappers are
|
|
267
|
+
* markup, not widgets — GtkBuilder's `<child type="sheet">` leaves nothing in
|
|
268
|
+
* the tree (adw-bottom-sheet.c:1278-1293) — so the wrapper itself is claimed
|
|
269
|
+
* too. It used to fall through to `unslotted` and be appended to the content
|
|
270
|
+
* layer, leaving an empty custom element behind after its children were
|
|
271
|
+
* pulled out.
|
|
272
|
+
*/
|
|
273
|
+
private _collectSlot(tag: string, slot: string, claimed: Set<Node>): Node[] {
|
|
274
|
+
const nodes: Node[] = [];
|
|
275
|
+
for (const child of Array.from(this.children)) {
|
|
276
|
+
if (child.tagName.toLowerCase() === tag) {
|
|
277
|
+
claimed.add(child);
|
|
278
|
+
nodes.push(...Array.from(child.childNodes));
|
|
279
|
+
} else if (child.getAttribute('slot') === slot) {
|
|
280
|
+
claimed.add(child);
|
|
281
|
+
nodes.push(child);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
return nodes;
|
|
200
285
|
}
|
|
201
286
|
}
|
|
202
287
|
|
|
@@ -1,67 +1,141 @@
|
|
|
1
1
|
// <adw-button-content> — a box-like child pairing a symbolic icon with a label,
|
|
2
|
-
// intended as the child of
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
// ellipsizes when set).
|
|
8
|
-
// Events: `notify::icon-name`, `notify::label`, `notify::
|
|
9
|
-
// (CustomEvent, bubbles) mirroring the AdwButtonContent
|
|
10
|
-
// the native story observes.
|
|
2
|
+
// intended as the child of a button (or Gtk.Button / MenuButton / SplitButton in
|
|
3
|
+
// libadwaita) when it needs both an icon and a label.
|
|
4
|
+
// Attributes: icon-name (symbolic name, e.g. "folder-download"; EMPTY shows the
|
|
5
|
+
// `image-missing` fallback, it does not hide the icon), label (empty hides the
|
|
6
|
+
// label), use-underline (boolean — `_` marks a mnemonic), can-shrink (boolean —
|
|
7
|
+
// label ellipsizes when set).
|
|
8
|
+
// Events: `notify::icon-name`, `notify::label`, `notify::use-underline`,
|
|
9
|
+
// `notify::can-shrink` (CustomEvent, bubbles) mirroring the AdwButtonContent
|
|
10
|
+
// GObject notify signals the native story observes.
|
|
11
|
+
//
|
|
12
|
+
// The derivations come from `@gjsify/adwaita-core` (ADR 0004). THE STYLE CLASS IS
|
|
13
|
+
// WHY THIS WAS TOUCHED: `AdwButtonContent` puts `image-text-button` on the button
|
|
14
|
+
// hosting it (adw-button-content.c:115) and removes it on unroot (:126), and the
|
|
15
|
+
// stylesheet gives that class `padding-left/right: 9px` where a plain text button
|
|
16
|
+
// has 17px (_buttons.scss:77-80 against :72-75). `grep -rn "image-text-button"`
|
|
17
|
+
// over this package used to return NOTHING, so every icon+label button here was
|
|
18
|
+
// drawn with a plain button's padding.
|
|
19
|
+
//
|
|
11
20
|
// Reference: refs/libadwaita/src/adw-button-content.c (AdwButtonContent)
|
|
12
|
-
// Reference: refs/libadwaita/src/stylesheet/widgets/_buttons.scss
|
|
21
|
+
// Reference: refs/libadwaita/src/stylesheet/widgets/_buttons.scss
|
|
22
|
+
// (image-text-button :77-91 · splitbutton override :499-507 ·
|
|
23
|
+
// buttoncontent :626-645)
|
|
13
24
|
// Copyright (c) GNOME contributors (libadwaita). LGPLv2.1+.
|
|
14
|
-
// Modifications: Implemented as a Web Component for @gjsify/adwaita-web
|
|
25
|
+
// Modifications: Implemented as a Web Component for @gjsify/adwaita-web; the
|
|
26
|
+
// icon node is <adw-icon>.
|
|
27
|
+
|
|
28
|
+
import { BUTTON_CONTENT_STYLE_CLASS, buttonContentIconName, buttonContentLabelText } from '@gjsify/adwaita-core';
|
|
29
|
+
|
|
30
|
+
import { type AdwIcon, createAdwIcon } from './adw-icon.js';
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* What `gtk_widget_get_ancestor (…, GTK_TYPE_BUTTON)` (:95) looks like in the
|
|
34
|
+
* DOM: a native `<button>`, or anything wearing the Adwaita button class (the
|
|
35
|
+
* `<adw-button>` element renders an inner `<button class="adw-button">`, and the
|
|
36
|
+
* stories style a plain one with the same class set).
|
|
37
|
+
*/
|
|
38
|
+
const BUTTON_SELECTOR = 'button, .adw-button';
|
|
39
|
+
|
|
40
|
+
/** The split-button node the class is RETARGETED to (:112-113). */
|
|
41
|
+
const SPLIT_BUTTON_SELECTOR = 'adw-split-button, .adw-split-button';
|
|
15
42
|
|
|
16
43
|
export class AdwButtonContent extends HTMLElement {
|
|
17
44
|
private _boxEl!: HTMLDivElement;
|
|
18
|
-
private _iconEl!:
|
|
45
|
+
private _iconEl!: AdwIcon;
|
|
19
46
|
private _labelEl!: HTMLSpanElement;
|
|
20
47
|
private _initialized = false;
|
|
48
|
+
/** The node currently carrying `image-text-button`, so unroot can find it. */
|
|
49
|
+
private _styledHost: HTMLElement | null = null;
|
|
21
50
|
|
|
22
51
|
static get observedAttributes() {
|
|
23
|
-
return ['icon-name', 'label', 'can-shrink'];
|
|
52
|
+
return ['icon-name', 'label', 'use-underline', 'can-shrink'];
|
|
24
53
|
}
|
|
25
54
|
|
|
26
55
|
connectedCallback() {
|
|
27
|
-
if (this._initialized)
|
|
28
|
-
|
|
56
|
+
if (!this._initialized) {
|
|
57
|
+
this._initialized = true;
|
|
29
58
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
59
|
+
// CSS node mirrors libadwaita's `buttoncontent > box > {image, label}`.
|
|
60
|
+
this._boxEl = document.createElement('div');
|
|
61
|
+
this._boxEl.className = 'adw-button-content-box';
|
|
33
62
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
63
|
+
// The image node is GTK_ACCESSIBLE_ROLE_PRESENTATION in libadwaita —
|
|
64
|
+
// it is decorative, the label carries the accessible name, which is
|
|
65
|
+
// what <adw-icon>'s own `aria-hidden` says.
|
|
66
|
+
this._iconEl = createAdwIcon(null);
|
|
38
67
|
|
|
39
|
-
|
|
40
|
-
|
|
68
|
+
this._labelEl = document.createElement('span');
|
|
69
|
+
this._labelEl.className = 'adw-button-content-label';
|
|
41
70
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
71
|
+
this._boxEl.append(this._iconEl, this._labelEl);
|
|
72
|
+
this.replaceChildren(this._boxEl);
|
|
73
|
+
this._render();
|
|
74
|
+
}
|
|
75
|
+
// `adw_button_content_root` runs on every rooting, not just the first
|
|
76
|
+
// (:99-116), so a re-parented content re-stamps its new host.
|
|
77
|
+
this._root();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
disconnectedCallback() {
|
|
81
|
+
this._unroot();
|
|
45
82
|
}
|
|
46
83
|
|
|
47
84
|
attributeChangedCallback(name: string) {
|
|
48
85
|
if (!this._initialized) return;
|
|
49
86
|
this._render();
|
|
50
87
|
// Mirror the AdwButtonContent notify::<prop> signals the native story
|
|
51
|
-
// listens to
|
|
88
|
+
// listens to.
|
|
52
89
|
this.dispatchEvent(new CustomEvent(`notify::${name}`, { bubbles: true }));
|
|
53
90
|
}
|
|
54
91
|
|
|
92
|
+
/**
|
|
93
|
+
* `adw_button_content_root` (:108-116): the nearest button ancestor, unless
|
|
94
|
+
* that button's DIRECT parent is a split button — then the split button
|
|
95
|
+
* takes the class, because `splitbutton.image-text-button > button` is the
|
|
96
|
+
* rule that styles it (_buttons.scss:499-507).
|
|
97
|
+
*/
|
|
98
|
+
private _hostButton(): HTMLElement | null {
|
|
99
|
+
const button = this.parentElement?.closest<HTMLElement>(BUTTON_SELECTOR) ?? null;
|
|
100
|
+
if (!button) return null;
|
|
101
|
+
const parent = button.parentElement;
|
|
102
|
+
return parent?.matches(SPLIT_BUTTON_SELECTOR) ? parent : button;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
private _root(): void {
|
|
106
|
+
const host = this._hostButton();
|
|
107
|
+
if (host === this._styledHost) return;
|
|
108
|
+
this._unroot();
|
|
109
|
+
// A content with no button ancestor styles nothing. The C would pass
|
|
110
|
+
// NULL to `gtk_widget_get_parent`/`add_css_class` here (:112-115) — two
|
|
111
|
+
// CRITICALs — so there is nothing to reproduce.
|
|
112
|
+
if (!host) return;
|
|
113
|
+
host.classList.add(BUTTON_CONTENT_STYLE_CLASS);
|
|
114
|
+
this._styledHost = host;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
private _unroot(): void {
|
|
118
|
+
// `adw_button_content_unroot` (:126) — a button that loses its content
|
|
119
|
+
// goes back to plain-button padding.
|
|
120
|
+
this._styledHost?.classList.remove(BUTTON_CONTENT_STYLE_CLASS);
|
|
121
|
+
this._styledHost = null;
|
|
122
|
+
}
|
|
123
|
+
|
|
55
124
|
private _render() {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
this
|
|
125
|
+
// An empty `icon-name` resolves to `image-missing` and the image node
|
|
126
|
+
// stays visible (:355-356). The docs at :228/:343 say the icon is not
|
|
127
|
+
// shown; the code never hides it, and this follows the code — see the
|
|
128
|
+
// `BUTTON_CONTENT_ICON_VECTORS` rule string.
|
|
129
|
+
this._iconEl.iconName = buttonContentIconName(this.getAttribute('icon-name') ?? '');
|
|
130
|
+
this._iconEl.hidden = false;
|
|
59
131
|
|
|
60
132
|
const label = this.getAttribute('label') ?? '';
|
|
61
|
-
this._labelEl.textContent = label;
|
|
133
|
+
this._labelEl.textContent = buttonContentLabelText(label, this.hasAttribute('use-underline'));
|
|
134
|
+
// `gtk_widget_set_visible (self->label, label[0])` (:398) — the RAW
|
|
135
|
+
// label decides, not the mnemonic-resolved text.
|
|
62
136
|
this._labelEl.hidden = label.length === 0;
|
|
63
137
|
|
|
64
|
-
// can-shrink → ellipsize the label (Gtk.Label PANGO_ELLIPSIZE_END).
|
|
138
|
+
// can-shrink → ellipsize the label (Gtk.Label PANGO_ELLIPSIZE_END, :490).
|
|
65
139
|
this.classList.toggle('can-shrink', this.hasAttribute('can-shrink'));
|
|
66
140
|
}
|
|
67
141
|
}
|
|
@@ -1,24 +1,49 @@
|
|
|
1
1
|
// <adw-button-row> — A boxed-list row that behaves like a button: a centered
|
|
2
|
-
// title
|
|
3
|
-
// `suggested-action` / `destructive-action` style
|
|
4
|
-
// matching Adwaita button variants.
|
|
5
|
-
// Attributes: title, start-icon-name (symbolic
|
|
6
|
-
//
|
|
2
|
+
// title between an optional start icon and an optional end icon, spanning the
|
|
3
|
+
// full width of the list. The `suggested-action` / `destructive-action` style
|
|
4
|
+
// classes recolor it like the matching Adwaita button variants.
|
|
5
|
+
// Attributes: title, start-icon-name, end-icon-name (symbolic names, e.g.
|
|
6
|
+
// "list-add" / "go-next").
|
|
7
7
|
// Events: `activated` (CustomEvent, bubbles) when the row is clicked — mirrors
|
|
8
8
|
// the Adw.ButtonRow `activated` GObject signal.
|
|
9
|
-
//
|
|
9
|
+
//
|
|
10
|
+
// The label/icon visibility rules are HEADLESS and live in
|
|
11
|
+
// `@gjsify/adwaita-core` (ADR 0004) as {@link ButtonRowState}; this element
|
|
12
|
+
// composes it and keeps only the DOM render half.
|
|
13
|
+
// `@gjsify/adwaita-nativescript` composes the same state, so both ports share one
|
|
14
|
+
// behaviour. Two things this fixes, both from adw-button-row.c:
|
|
15
|
+
// - `end-icon-name` (C:213-223, bound at adw-button-row.ui:52-65) exists since
|
|
16
|
+
// libadwaita 1.6 and neither renderer had it, so the trailing-chevron shape
|
|
17
|
+
// the property exists for could not be expressed.
|
|
18
|
+
// - THERE IS NO `activatable` OPT-OUT. `<property name="activatable">True
|
|
19
|
+
// </property>` is in the template (adw-button-row.ui:5), the class docs say
|
|
20
|
+
// "AdwButtonRow is always activatable." (C:31), and the whole public surface
|
|
21
|
+
// (C:270-352) is `new` plus the two icon-name pairs. This element used to
|
|
22
|
+
// honour an invented `activatable="false"`, which also gave the same markup
|
|
23
|
+
// two opposite meanings inside one package — `<adw-action-row>` reads
|
|
24
|
+
// `activatable` by PRESENCE, so `activatable="false"` there meant TRUE.
|
|
25
|
+
//
|
|
26
|
+
// Reference: refs/libadwaita/src/adw-button-row.c, adw-button-row.ui
|
|
10
27
|
// Reference: refs/libadwaita/src/stylesheet/widgets/_lists.scss (row.button)
|
|
11
28
|
// Copyright (c) GNOME contributors (libadwaita). LGPLv2.1+.
|
|
12
|
-
// Modifications: Implemented as a Web Component for @gjsify/adwaita-web
|
|
29
|
+
// Modifications: Implemented as a Web Component for @gjsify/adwaita-web; the
|
|
30
|
+
// label/icon state composed from @gjsify/adwaita-core.
|
|
31
|
+
|
|
32
|
+
import { BUTTON_ROW_ACTIVATABLE, ButtonRowState } from '@gjsify/adwaita-core';
|
|
33
|
+
|
|
34
|
+
import { type AdwIcon, createAdwIcon } from './adw-icon.js';
|
|
13
35
|
|
|
14
36
|
export class AdwButtonRow extends HTMLElement {
|
|
15
37
|
private _contentsEl!: HTMLDivElement;
|
|
16
|
-
private _startIconEl!:
|
|
38
|
+
private _startIconEl!: AdwIcon;
|
|
39
|
+
private _endIconEl!: AdwIcon;
|
|
17
40
|
private _titleEl!: HTMLSpanElement;
|
|
41
|
+
/** The headless title + start/end icon state (ADR 0004). */
|
|
42
|
+
private readonly _state = new ButtonRowState();
|
|
18
43
|
private _initialized = false;
|
|
19
44
|
|
|
20
45
|
static get observedAttributes() {
|
|
21
|
-
return ['title', 'start-icon-name', '
|
|
46
|
+
return ['title', 'start-icon-name', 'end-icon-name'];
|
|
22
47
|
}
|
|
23
48
|
|
|
24
49
|
connectedCallback() {
|
|
@@ -27,33 +52,21 @@ export class AdwButtonRow extends HTMLElement {
|
|
|
27
52
|
this._contentsEl = document.createElement('div');
|
|
28
53
|
this._contentsEl.className = 'adw-button-row-contents';
|
|
29
54
|
|
|
30
|
-
this._startIconEl =
|
|
31
|
-
this._startIconEl.setAttribute('aria-hidden', 'true');
|
|
55
|
+
this._startIconEl = createAdwIcon(null, 'start');
|
|
32
56
|
|
|
33
57
|
this._titleEl = document.createElement('span');
|
|
34
58
|
this._titleEl.className = 'adw-button-row-title';
|
|
35
59
|
|
|
36
|
-
this.
|
|
37
|
-
this.replaceChildren(this._contentsEl);
|
|
60
|
+
this._endIconEl = createAdwIcon(null, 'end');
|
|
38
61
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
// so the re-entrant attributeChangedCallback is a no-op — the explicit
|
|
42
|
-
// _render() below does the first render, once the elements above exist.
|
|
43
|
-
// (Setting _initialized before the elements were built made this
|
|
44
|
-
// setAttribute re-enter _render() and crash on a declaratively-parsed
|
|
45
|
-
// row, where connectedCallback runs with attributes already present.)
|
|
46
|
-
if (!this.hasAttribute('activatable')) {
|
|
47
|
-
this.setAttribute('activatable', '');
|
|
48
|
-
}
|
|
62
|
+
this._contentsEl.append(this._startIconEl, this._titleEl, this._endIconEl);
|
|
63
|
+
this.replaceChildren(this._contentsEl);
|
|
49
64
|
|
|
50
65
|
this._initialized = true;
|
|
51
66
|
this._render();
|
|
52
67
|
|
|
53
68
|
this.addEventListener('click', () => {
|
|
54
|
-
|
|
55
|
-
this.dispatchEvent(new CustomEvent('activated', { bubbles: true }));
|
|
56
|
-
}
|
|
69
|
+
this.dispatchEvent(new CustomEvent('activated', { bubbles: true }));
|
|
57
70
|
});
|
|
58
71
|
}
|
|
59
72
|
|
|
@@ -61,20 +74,31 @@ export class AdwButtonRow extends HTMLElement {
|
|
|
61
74
|
if (this._initialized) this._render();
|
|
62
75
|
}
|
|
63
76
|
|
|
64
|
-
private _isActivatable(): boolean {
|
|
65
|
-
return this.getAttribute('activatable') !== 'false';
|
|
66
|
-
}
|
|
67
|
-
|
|
68
77
|
private _render() {
|
|
69
|
-
|
|
70
|
-
this.
|
|
71
|
-
this.
|
|
78
|
+
this._state.setTitle(this.getAttribute('title'));
|
|
79
|
+
this._state.setStartIconName(this.getAttribute('start-icon-name'));
|
|
80
|
+
this._state.setEndIconName(this.getAttribute('end-icon-name'));
|
|
81
|
+
const state = this._state.state;
|
|
82
|
+
|
|
83
|
+
this._titleEl.textContent = state.title;
|
|
84
|
+
this._titleEl.hidden = !state.titleVisible;
|
|
72
85
|
|
|
73
|
-
|
|
74
|
-
this.
|
|
75
|
-
|
|
86
|
+
this._paintIcon(this._startIconEl, state.startIconName, state.startIconVisible);
|
|
87
|
+
this._paintIcon(this._endIconEl, state.endIconName, state.endIconVisible);
|
|
88
|
+
|
|
89
|
+
// Unconditional: an AdwButtonRow has no way to not be activatable.
|
|
90
|
+
this.classList.toggle('activatable', BUTTON_ROW_ACTIVATABLE);
|
|
91
|
+
}
|
|
76
92
|
|
|
77
|
-
|
|
93
|
+
/**
|
|
94
|
+
* Paint one of the two `image.icon.{start,end}` nodes (adw-button-row.c:39-40).
|
|
95
|
+
*
|
|
96
|
+
* The `start` / `end` position class is set once at construction — it is
|
|
97
|
+
* where the node sits, not what it draws — so only the name changes here.
|
|
98
|
+
*/
|
|
99
|
+
private _paintIcon(el: AdwIcon, iconName: string, visible: boolean) {
|
|
100
|
+
el.iconName = visible ? iconName : null;
|
|
101
|
+
el.hidden = !visible;
|
|
78
102
|
}
|
|
79
103
|
}
|
|
80
104
|
|