@gjsify/adwaita-web 0.51.1 → 0.52.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 +255 -27
- package/dist/adwaita-web.css.map +1 -1
- package/lib/types/blueprint-layout.spec.d.ts +1 -0
- package/lib/types/blueprint-markup.spec.d.ts +4 -0
- package/lib/types/blueprint-tree.spec.d.ts +1 -0
- package/lib/types/elements/adw-bottom-sheet.d.ts +31 -13
- package/lib/types/elements/adw-carousel.d.ts +5 -0
- package/lib/types/elements/adw-header-bar.d.ts +15 -0
- package/lib/types/elements/adw-inline-view-switcher.d.ts +1 -0
- package/lib/types/elements/adw-navigation-view.d.ts +1 -0
- package/lib/types/elements/adw-sidebar.d.ts +10 -0
- package/lib/types/elements/adw-tab-view.d.ts +1 -0
- package/lib/types/elements/adw-toggle-group.d.ts +11 -0
- package/lib/types/elements/gtk-action-bar.d.ts +19 -0
- package/lib/types/elements/gtk-box.d.ts +17 -0
- package/lib/types/elements/gtk-button.d.ts +2 -0
- package/lib/types/elements/gtk-label.d.ts +122 -0
- package/lib/types/gio-menu.spec.d.ts +1 -0
- package/lib/types/gtk-box.spec.d.ts +1 -0
- package/lib/types/gtk-label.spec.d.ts +1 -0
- package/lib/types/gtk-value-doors.spec.d.ts +1 -0
- package/lib/types/index.d.ts +4 -1
- package/lib/types/namespace/gio.d.ts +1 -0
- package/lib/types/namespace/gtk.d.ts +5 -0
- package/lib/types/shared-tree-builder.d.ts +64 -0
- package/lib/types/source-view/asm6502.d.ts +1 -20
- package/lib/types/source-view/index.d.ts +3 -2
- package/lib/types/source-view/theme.d.ts +1 -1
- package/lib/types/string-list-slot.d.ts +4 -0
- package/lib/types/styles.generated.d.ts +1 -1
- package/lib/types/tags.spec.d.ts +1 -0
- package/lib/types/value-lists.spec.d.ts +1 -0
- package/package.json +8 -6
- package/scss/_action_bar.scss +51 -0
- package/scss/_banner.scss +8 -1
- package/scss/_bottom_sheet.scss +57 -0
- package/scss/_box.scss +23 -0
- package/scss/_button.scss +29 -7
- package/scss/_button_content.scss +6 -1
- package/scss/_button_row.scss +5 -2
- package/scss/_carousel.scss +6 -0
- package/scss/_labels.scss +126 -12
- package/scss/_navigation_split_view.scss +18 -0
- package/scss/_overlay_split_view.scss +5 -0
- package/scss/_reset.scss +29 -4
- package/scss/_split_button.scss +5 -2
- package/scss/_status_page.scss +31 -17
- package/scss/_theme.scss +11 -3
- package/scss/_toolbar_view.scss +10 -0
- package/scss/_variables.scss +1 -0
- package/scss/_widget.scss +55 -0
- package/scss/_window_title.scss +9 -0
- package/scss/adwaita-skin.scss +8 -2
- package/src/adw-banner.spec.ts +16 -0
- package/src/adw-button-row.spec.ts +8 -0
- package/src/adw-header-bar.spec.ts +17 -0
- package/src/blueprint-layout.spec.ts +223 -0
- package/src/blueprint-markup.spec.ts +155 -0
- package/src/blueprint-tree.spec.ts +174 -0
- package/src/bottom-sheet.spec.ts +166 -1
- package/src/chrome.spec.ts +5 -2
- package/src/elements/adw-bottom-sheet.ts +166 -59
- package/src/elements/adw-carousel.ts +10 -5
- package/src/elements/adw-clamp.ts +12 -0
- package/src/elements/adw-combo-row.ts +13 -4
- package/src/elements/adw-header-bar.ts +46 -3
- package/src/elements/adw-inline-view-switcher.ts +11 -0
- package/src/elements/adw-navigation-view.ts +10 -0
- package/src/elements/adw-sidebar.ts +22 -0
- package/src/elements/adw-spin-row.ts +44 -5
- package/src/elements/adw-status-page.ts +12 -6
- package/src/elements/adw-tab-view.ts +20 -3
- package/src/elements/adw-toggle-group.ts +45 -4
- package/src/elements/adw-toolbar-view.ts +9 -0
- package/src/elements/gtk-action-bar.ts +90 -0
- package/src/elements/gtk-box.ts +87 -0
- package/src/elements/gtk-button.ts +60 -13
- package/src/elements/gtk-drop-down.ts +10 -1
- package/src/elements/gtk-label.ts +415 -0
- package/src/gio-menu.spec.ts +125 -0
- package/src/globals.d.ts +7 -0
- package/src/gtk-box.spec.ts +168 -0
- package/src/gtk-button.spec.ts +123 -1
- package/src/gtk-label.spec.ts +595 -0
- package/src/gtk-value-doors.spec.ts +104 -0
- package/src/index.ts +14 -1
- package/src/keyboard-operable.spec.ts +2 -1
- package/src/namespace/gio.ts +19 -0
- package/src/namespace/gtk.ts +14 -0
- package/src/shared-tree-builder.ts +268 -0
- package/src/shared-trees.spec.ts +296 -32
- package/src/source-view/adw-source-view.ts +3 -3
- package/src/source-view/asm6502.ts +11 -191
- package/src/source-view/index.ts +17 -11
- package/src/source-view/theme.ts +16 -16
- package/src/split-button.spec.ts +7 -0
- package/src/string-list-slot.ts +21 -0
- package/src/styles.generated.ts +1 -1
- package/src/tags.spec.ts +51 -0
- package/src/test.browser.mts +18 -0
- package/src/value-lists.spec.ts +93 -0
- package/lib/types/source-view/hex.d.ts +0 -21
- package/src/source-view/hex.ts +0 -37
|
@@ -15,19 +15,31 @@
|
|
|
15
15
|
// attribute to another truthy spelling notifies nobody.
|
|
16
16
|
// - the drag handle is decorative, not a close button — see below.
|
|
17
17
|
//
|
|
18
|
-
// Children are declared as <adw-bottom-sheet-content> (the persistent content)
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
//
|
|
18
|
+
// Children are declared as <adw-bottom-sheet-content> (the persistent content),
|
|
19
|
+
// <adw-bottom-sheet-sheet> (the slide-up sheet) and <adw-bottom-sheet-bottom-bar>
|
|
20
|
+
// (the collapsed bar shown in its place); all three are consumed at connect time.
|
|
21
|
+
// Unslotted children fall back to the content, mirroring the AdwBottomSheet
|
|
22
|
+
// GtkBuildable default (omitting the child type sets content).
|
|
23
|
+
//
|
|
24
|
+
// THE BOTTOM BAR IS THE AFFORDANCE. It is the only thing libadwaita lets a user
|
|
25
|
+
// click to OPEN a sheet, so an element without one can be opened by its owner and
|
|
26
|
+
// by nobody else. It is a real <button>, because the bin upstream is a GtkButton
|
|
27
|
+
// (adw-bottom-sheet.c:1203) — that is where the keyboard activation comes from.
|
|
22
28
|
//
|
|
23
29
|
// Attributes:
|
|
24
|
-
// open
|
|
25
|
-
// modal
|
|
26
|
-
// can-close
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
-
//
|
|
30
|
-
//
|
|
30
|
+
// open (boolean — whether the sheet is revealed; default closed)
|
|
31
|
+
// modal (boolean — dim + block the content while open; default on)
|
|
32
|
+
// can-close (boolean — user can dismiss via the dimming / Esc / the
|
|
33
|
+
// sheet.close action; default on. When off, a dismissal
|
|
34
|
+
// raises `close-attempt` instead of closing — mirrors
|
|
35
|
+
// Adw.BottomSheet:can-close)
|
|
36
|
+
// can-open (boolean — user can open the sheet from the bottom bar;
|
|
37
|
+
// default on. When off the bar stays on screen and gains
|
|
38
|
+
// the `inert` class, because libadwaita refuses the click
|
|
39
|
+
// without disabling the button — Adw.BottomSheet:can-open)
|
|
40
|
+
// reveal-bottom-bar (boolean — whether the bottom bar is shown at all while the
|
|
41
|
+
// sheet is closed; default on)
|
|
42
|
+
// show-drag-handle (boolean — overlay the drag handle on the sheet; default on)
|
|
31
43
|
//
|
|
32
44
|
// Events:
|
|
33
45
|
// `notify::open` (CustomEvent, bubbles, `detail = { open }`) when the revealed
|
|
@@ -44,7 +56,14 @@
|
|
|
44
56
|
// Modifications: Implemented as a Web Component for @gjsify/adwaita-web.
|
|
45
57
|
|
|
46
58
|
import { BottomSheetPresentation } from '@gjsify/adwaita-core';
|
|
47
|
-
import type {
|
|
59
|
+
import type {
|
|
60
|
+
BottomSheetCloseOutcome,
|
|
61
|
+
BottomSheetCloseSource,
|
|
62
|
+
BottomSheetOpenOutcome,
|
|
63
|
+
BottomSheetOpenSource,
|
|
64
|
+
} from '@gjsify/adwaita-core';
|
|
65
|
+
|
|
66
|
+
import { bindSlottedChildren, type AdwSlot } from '../slotted-children.js';
|
|
48
67
|
|
|
49
68
|
/** The persistent content. Child of <adw-bottom-sheet>; consumed at connect time. */
|
|
50
69
|
export class AdwBottomSheetContent extends HTMLElement {}
|
|
@@ -52,19 +71,26 @@ export class AdwBottomSheetContent extends HTMLElement {}
|
|
|
52
71
|
/** The slide-up sheet. Child of <adw-bottom-sheet>; consumed at connect time. */
|
|
53
72
|
export class AdwBottomSheetSheet extends HTMLElement {}
|
|
54
73
|
|
|
74
|
+
/** The collapsed bar the sheet morphs out of. Child of <adw-bottom-sheet>. */
|
|
75
|
+
export class AdwBottomSheetBottomBar extends HTMLElement {}
|
|
76
|
+
|
|
55
77
|
export class AdwBottomSheet extends HTMLElement {
|
|
56
78
|
private _initialized = false;
|
|
57
79
|
private _contentEl!: HTMLDivElement;
|
|
58
80
|
private _dimmingEl!: HTMLDivElement;
|
|
59
81
|
private _sheetEl!: HTMLDivElement;
|
|
82
|
+
private _sheetBodyEl!: HTMLDivElement;
|
|
83
|
+
private _bottomBarEl!: HTMLButtonElement;
|
|
60
84
|
private _dragHandleEl!: HTMLDivElement;
|
|
85
|
+
/** A bar set through {@link setBottomBar} before the element upgraded. */
|
|
86
|
+
private _pendingBottomBar: Node | null = null;
|
|
61
87
|
/** The shared open/can-close model — the single source of truth for both. */
|
|
62
88
|
private readonly _state = new BottomSheetPresentation();
|
|
63
89
|
/** Set while the element writes `open` back, so the write does not re-enter. */
|
|
64
90
|
private _reflecting = false;
|
|
65
91
|
|
|
66
92
|
static get observedAttributes() {
|
|
67
|
-
return ['open', 'modal', 'can-close', 'show-drag-handle'];
|
|
93
|
+
return ['open', 'modal', 'can-close', 'can-open', 'reveal-bottom-bar', 'show-drag-handle'];
|
|
68
94
|
}
|
|
69
95
|
|
|
70
96
|
get open(): boolean {
|
|
@@ -75,8 +101,9 @@ export class AdwBottomSheet extends HTMLElement {
|
|
|
75
101
|
this._setOpen(!!value);
|
|
76
102
|
}
|
|
77
103
|
|
|
104
|
+
/** Whether the content is dimmed and blocked while the sheet is open (`AdwBottomSheet:modal`). */
|
|
78
105
|
get modal(): boolean {
|
|
79
|
-
return this.
|
|
106
|
+
return this._state.modal;
|
|
80
107
|
}
|
|
81
108
|
|
|
82
109
|
set modal(value: boolean) {
|
|
@@ -92,6 +119,24 @@ export class AdwBottomSheet extends HTMLElement {
|
|
|
92
119
|
this._setBoolAttr('can-close', value);
|
|
93
120
|
}
|
|
94
121
|
|
|
122
|
+
/** Whether the user can open the sheet from its bottom bar (`Adw.BottomSheet:can-open`). */
|
|
123
|
+
get canOpen(): boolean {
|
|
124
|
+
return this._state.canOpen;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
set canOpen(value: boolean) {
|
|
128
|
+
this._setBoolAttr('can-open', value);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** Whether the bottom bar is shown while the sheet is closed. */
|
|
132
|
+
get revealBottomBar(): boolean {
|
|
133
|
+
return this._state.revealBottomBar;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
set revealBottomBar(value: boolean) {
|
|
137
|
+
this._setBoolAttr('reveal-bottom-bar', value);
|
|
138
|
+
}
|
|
139
|
+
|
|
95
140
|
get showDragHandle(): boolean {
|
|
96
141
|
return this._boolAttr('show-drag-handle', true);
|
|
97
142
|
}
|
|
@@ -100,6 +145,38 @@ export class AdwBottomSheet extends HTMLElement {
|
|
|
100
145
|
this._setBoolAttr('show-drag-handle', value);
|
|
101
146
|
}
|
|
102
147
|
|
|
148
|
+
/** The bar's content, or `null` when the sheet has none — the read-back for {@link setBottomBar}. */
|
|
149
|
+
get bottomBar(): Node | null {
|
|
150
|
+
return this._initialized ? this._bottomBarEl.firstChild : this._pendingBottomBar;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Give the sheet a bottom bar, or take it away — `adw_bottom_sheet_set_bottom_bar`.
|
|
155
|
+
*
|
|
156
|
+
* Presence is an INPUT to the open gate, not decoration: removing the bar removes every
|
|
157
|
+
* way a user has of opening this sheet, which is why the state hears about it rather
|
|
158
|
+
* than only the DOM.
|
|
159
|
+
*/
|
|
160
|
+
setBottomBar(child: Node | null): void {
|
|
161
|
+
this._state.setHasBottomBar(child !== null);
|
|
162
|
+
if (!this._initialized) {
|
|
163
|
+
this._pendingBottomBar = child;
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
if (child) this._bottomBarEl.replaceChildren(child);
|
|
167
|
+
else this._bottomBarEl.replaceChildren();
|
|
168
|
+
this._render();
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Route an open affordance through the shared gate. `'ignored'` is silent by design:
|
|
173
|
+
* libadwaita has no `open-attempt` counterpart to `close-attempt`, so a refused open
|
|
174
|
+
* emits nothing at all.
|
|
175
|
+
*/
|
|
176
|
+
requestOpen(source: BottomSheetOpenSource): BottomSheetOpenOutcome {
|
|
177
|
+
return this._state.requestOpen(source);
|
|
178
|
+
}
|
|
179
|
+
|
|
103
180
|
/**
|
|
104
181
|
* Route a dismissal affordance through the shared gate and act on the verdict: close,
|
|
105
182
|
* raise `close-attempt`, forward `sheet.close` to an ancestor, or do nothing. Called
|
|
@@ -123,17 +200,8 @@ export class AdwBottomSheet extends HTMLElement {
|
|
|
123
200
|
if (this._initialized) return;
|
|
124
201
|
this._initialized = true;
|
|
125
202
|
|
|
126
|
-
// The sheet and content are placed by slot; anything unslotted falls back to the
|
|
127
|
-
// content (the AdwBottomSheet GtkBuildable default).
|
|
128
|
-
const claimed = new Set<Node>();
|
|
129
|
-
const sheetChildren = this._collectSlot('adw-bottom-sheet-sheet', 'sheet', claimed);
|
|
130
|
-
const contentChildren = this._collectSlot('adw-bottom-sheet-content', 'content', claimed);
|
|
131
|
-
const unslotted = Array.from(this.childNodes).filter((n) => !claimed.has(n));
|
|
132
|
-
|
|
133
203
|
this._contentEl = document.createElement('div');
|
|
134
204
|
this._contentEl.className = 'adw-bottom-sheet-content';
|
|
135
|
-
for (const child of contentChildren) this._contentEl.appendChild(child);
|
|
136
|
-
for (const child of unslotted) this._contentEl.appendChild(child);
|
|
137
205
|
|
|
138
206
|
this._dimmingEl = document.createElement('div');
|
|
139
207
|
this._dimmingEl.className = 'adw-bottom-sheet-dimming';
|
|
@@ -151,13 +219,57 @@ export class AdwBottomSheet extends HTMLElement {
|
|
|
151
219
|
this._dragHandleEl.className = 'adw-bottom-sheet-drag-handle';
|
|
152
220
|
this._dragHandleEl.setAttribute('aria-hidden', 'true');
|
|
153
221
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
222
|
+
this._sheetBodyEl = document.createElement('div');
|
|
223
|
+
this._sheetBodyEl.className = 'adw-bottom-sheet-sheet-body';
|
|
224
|
+
|
|
225
|
+
// A real <button>: the bin upstream is `gtk_button_new ()`
|
|
226
|
+
// (adw-bottom-sheet.c:1203), which is where its keyboard activation comes from
|
|
227
|
+
// (`bottom_bar_clicked_cb`, :402-407) — a div with a click handler would have the
|
|
228
|
+
// pointer door and not that one. It is the sheet bin's OTHER stack child, so it
|
|
229
|
+
// lives inside the sheet element and the two swap rather than stacking.
|
|
230
|
+
this._bottomBarEl = document.createElement('button');
|
|
231
|
+
this._bottomBarEl.type = 'button';
|
|
232
|
+
this._bottomBarEl.className = 'adw-bottom-sheet-bottom-bar';
|
|
233
|
+
this._bottomBarEl.addEventListener('click', () => this.requestOpen('bottom-bar'));
|
|
234
|
+
|
|
235
|
+
this._sheetEl.append(this._bottomBarEl, this._dragHandleEl, this._sheetBodyEl);
|
|
236
|
+
|
|
237
|
+
// The three layers are SLOTS, bound live (`slotted-children.ts`), under the names
|
|
238
|
+
// GtkBuilder gives the properties — `content`, `sheet`, `bottom-bar` — so a projected
|
|
239
|
+
// `.blp` places its children here, and the mount's refusal of an unknown slot can
|
|
240
|
+
// ask this element which names it has. The wrapper elements are the markup spelling
|
|
241
|
+
// of the same three: each is consumed and its children land in the layer. Anything
|
|
242
|
+
// unslotted is content, the AdwBottomSheet GtkBuildable default.
|
|
243
|
+
const layer = (tag: string, into: HTMLElement): AdwSlot => ({
|
|
244
|
+
claims: (node) => node instanceof Element && node.localName === tag,
|
|
245
|
+
consume: (node) => {
|
|
246
|
+
for (const child of Array.from(node.childNodes)) into.appendChild(child);
|
|
247
|
+
},
|
|
248
|
+
});
|
|
249
|
+
bindSlottedChildren(
|
|
250
|
+
this,
|
|
251
|
+
[
|
|
252
|
+
{ name: 'sheet', into: this._sheetBodyEl },
|
|
253
|
+
{ name: 'bottom-bar', into: this._bottomBarEl },
|
|
254
|
+
{ name: 'content', into: this._contentEl },
|
|
255
|
+
layer('adw-bottom-sheet-sheet', this._sheetBodyEl),
|
|
256
|
+
layer('adw-bottom-sheet-bottom-bar', this._bottomBarEl),
|
|
257
|
+
layer('adw-bottom-sheet-content', this._contentEl),
|
|
258
|
+
{ into: this._contentEl },
|
|
259
|
+
],
|
|
260
|
+
(_node, slot) => {
|
|
261
|
+
// A bar adopted after connect is presence, which the open gate reads.
|
|
262
|
+
if (!('into' in slot) || slot.into !== this._bottomBarEl) return;
|
|
263
|
+
this._state.setHasBottomBar(true);
|
|
264
|
+
this._render();
|
|
265
|
+
},
|
|
266
|
+
).install(this._contentEl, this._dimmingEl, this._sheetEl);
|
|
267
|
+
|
|
268
|
+
// A bar handed to `setBottomBar` before the upgrade wins over a declared one, as the
|
|
269
|
+
// later write does in GTK. `hasBottomBar` is re-seeded from the bin below, so the two
|
|
270
|
+
// cannot start out disagreeing about whether there is a bar.
|
|
271
|
+
if (this._pendingBottomBar) this._bottomBarEl.replaceChildren(this._pendingBottomBar);
|
|
272
|
+
this._pendingBottomBar = null;
|
|
161
273
|
|
|
162
274
|
// Escape → `maybe_close_cb`. In GTK the shortcut sits on the sheet itself, so it
|
|
163
275
|
// fires exactly while the sheet has focus — INCLUDING while the sheet is closed,
|
|
@@ -174,6 +286,10 @@ export class AdwBottomSheet extends HTMLElement {
|
|
|
174
286
|
// Seeding BEFORE the subscription keeps the initial value silent, which is what
|
|
175
287
|
// an attribute set during upgrade must be.
|
|
176
288
|
this._state.setCanClose(this._boolAttr('can-close', true));
|
|
289
|
+
this._state.setCanOpen(this._boolAttr('can-open', true));
|
|
290
|
+
this._state.setRevealBottomBar(this._boolAttr('reveal-bottom-bar', true));
|
|
291
|
+
// A declared bar counts as present even where nothing called `setBottomBar`.
|
|
292
|
+
this._state.setHasBottomBar(this._bottomBarEl.childNodes.length > 0);
|
|
177
293
|
this._state.setOpen(this._boolAttr('open', false));
|
|
178
294
|
this._state.subscribe((open) => {
|
|
179
295
|
this._render();
|
|
@@ -196,7 +312,10 @@ export class AdwBottomSheet extends HTMLElement {
|
|
|
196
312
|
this._state.setOpen(this._boolAttr('open', false));
|
|
197
313
|
return;
|
|
198
314
|
}
|
|
315
|
+
if (name === 'modal') this._state.setModal(this._boolAttr('modal', true));
|
|
199
316
|
if (name === 'can-close') this._state.setCanClose(this._boolAttr('can-close', true));
|
|
317
|
+
if (name === 'can-open') this._state.setCanOpen(this._boolAttr('can-open', true));
|
|
318
|
+
if (name === 'reveal-bottom-bar') this._state.setRevealBottomBar(this._boolAttr('reveal-bottom-bar', true));
|
|
200
319
|
if (this._initialized) this._render();
|
|
201
320
|
}
|
|
202
321
|
|
|
@@ -211,13 +330,25 @@ export class AdwBottomSheet extends HTMLElement {
|
|
|
211
330
|
|
|
212
331
|
private _render(): void {
|
|
213
332
|
const open = this._state.open;
|
|
333
|
+
const chrome = this._state.chrome;
|
|
334
|
+
const showsBar = chrome.layer === 'bottom-bar';
|
|
214
335
|
this._reflectOpen(open);
|
|
215
336
|
this.classList.toggle('open', open);
|
|
216
|
-
this.classList.toggle('modal', this.modal);
|
|
337
|
+
this.classList.toggle('modal', this._state.modal);
|
|
217
338
|
this.classList.toggle('has-drag-handle', this.showDragHandle);
|
|
218
|
-
|
|
219
|
-
this
|
|
220
|
-
|
|
339
|
+
// `showing-bottom-bar` is what holds the sheet at translateY(0) with only the bar
|
|
340
|
+
// exposed; the CSS transform IS this port's `gtk_widget_set_child_visible`, so the
|
|
341
|
+
// bar being hidden and the sheet being off screen are two different states.
|
|
342
|
+
this.classList.toggle('showing-bottom-bar', showsBar && chrome.surfaceVisible);
|
|
343
|
+
|
|
344
|
+
this._dimmingEl.classList.toggle('visible', chrome.dimmed);
|
|
345
|
+
this._bottomBarEl.hidden = !showsBar;
|
|
346
|
+
// `inert` only paints. The button stays enabled and focusable exactly as upstream
|
|
347
|
+
// leaves it (adw-bottom-sheet.c:2033-2036); the gate is what refuses the click, and
|
|
348
|
+
// `disabled` here would take the focus stop away with it.
|
|
349
|
+
this._bottomBarEl.classList.toggle('inert', chrome.bottomBarInert);
|
|
350
|
+
this._sheetBodyEl.hidden = showsBar;
|
|
351
|
+
this._dragHandleEl.hidden = !this.showDragHandle || showsBar;
|
|
221
352
|
}
|
|
222
353
|
|
|
223
354
|
private _reflectOpen(open: boolean): void {
|
|
@@ -237,33 +368,9 @@ export class AdwBottomSheet extends HTMLElement {
|
|
|
237
368
|
if (value) this.setAttribute(name, '');
|
|
238
369
|
else this.setAttribute(name, 'false');
|
|
239
370
|
}
|
|
240
|
-
|
|
241
|
-
/**
|
|
242
|
-
* Collect the nodes declared for one slot, in DOCUMENT order, marking every
|
|
243
|
-
* consumed child in `claimed`.
|
|
244
|
-
*
|
|
245
|
-
* The `<adw-bottom-sheet-sheet>` / `<adw-bottom-sheet-content>` wrappers are
|
|
246
|
-
* markup, not widgets — GtkBuilder's `<child type="sheet">` leaves nothing in
|
|
247
|
-
* the tree — so the wrapper itself is claimed
|
|
248
|
-
* too. It used to fall through to `unslotted` and be appended to the content
|
|
249
|
-
* layer, leaving an empty custom element behind after its children were
|
|
250
|
-
* pulled out.
|
|
251
|
-
*/
|
|
252
|
-
private _collectSlot(tag: string, slot: string, claimed: Set<Node>): Node[] {
|
|
253
|
-
const nodes: Node[] = [];
|
|
254
|
-
for (const child of Array.from(this.children)) {
|
|
255
|
-
if (child.tagName.toLowerCase() === tag) {
|
|
256
|
-
claimed.add(child);
|
|
257
|
-
nodes.push(...Array.from(child.childNodes));
|
|
258
|
-
} else if (child.getAttribute('slot') === slot) {
|
|
259
|
-
claimed.add(child);
|
|
260
|
-
nodes.push(child);
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
return nodes;
|
|
264
|
-
}
|
|
265
371
|
}
|
|
266
372
|
|
|
267
373
|
customElements.define('adw-bottom-sheet-content', AdwBottomSheetContent);
|
|
268
374
|
customElements.define('adw-bottom-sheet-sheet', AdwBottomSheetSheet);
|
|
375
|
+
customElements.define('adw-bottom-sheet-bottom-bar', AdwBottomSheetBottomBar);
|
|
269
376
|
customElements.define('adw-bottom-sheet', AdwBottomSheet);
|
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
// `page-changed` (`detail = { index }`) fires on every settle, including back
|
|
23
23
|
// onto the same page, and reports -1 when empty.
|
|
24
24
|
//
|
|
25
|
-
// The indicators bind through their `carousel` property or a `
|
|
26
|
-
// naming the carousel's id.
|
|
25
|
+
// The indicators bind through their `carousel` property, or a `carousel` (GTK's
|
|
26
|
+
// name, what a projected `.blp` writes) or `for` attribute naming the carousel's id.
|
|
27
27
|
//
|
|
28
28
|
// Reference: refs/libadwaita/src/adw-carousel.c (AdwCarousel behaviour)
|
|
29
29
|
// Reference: refs/libadwaita/src/adw-carousel-indicator-dots.c
|
|
@@ -678,7 +678,7 @@ abstract class AdwCarouselIndicator extends HTMLElement {
|
|
|
678
678
|
protected _initialized = false;
|
|
679
679
|
|
|
680
680
|
static get observedAttributes() {
|
|
681
|
-
return ['for'];
|
|
681
|
+
return ['for', 'carousel'];
|
|
682
682
|
}
|
|
683
683
|
|
|
684
684
|
/** The bound carousel. Mirrors AdwCarouselIndicator*'s `carousel` property. */
|
|
@@ -705,7 +705,7 @@ abstract class AdwCarouselIndicator extends HTMLElement {
|
|
|
705
705
|
}
|
|
706
706
|
|
|
707
707
|
attributeChangedCallback(name: string) {
|
|
708
|
-
if (name === 'for' && this._initialized) {
|
|
708
|
+
if ((name === 'for' || name === 'carousel') && this._initialized) {
|
|
709
709
|
this._detach();
|
|
710
710
|
this._carousel = null;
|
|
711
711
|
this._resolveForAttr();
|
|
@@ -714,8 +714,13 @@ abstract class AdwCarouselIndicator extends HTMLElement {
|
|
|
714
714
|
}
|
|
715
715
|
}
|
|
716
716
|
|
|
717
|
+
/**
|
|
718
|
+
* The carousel named by id. `carousel` is the GTK property's own name, and what a
|
|
719
|
+
* projected `.blp` writes for `carousel: carousel`; `for` is the markup spelling this
|
|
720
|
+
* element had first, kept as the fallback.
|
|
721
|
+
*/
|
|
717
722
|
private _resolveForAttr(): void {
|
|
718
|
-
const id = this.getAttribute('for');
|
|
723
|
+
const id = this.getAttribute('carousel') ?? this.getAttribute('for');
|
|
719
724
|
if (!id) return;
|
|
720
725
|
const root = this.getRootNode() as Document | ShadowRoot;
|
|
721
726
|
const el = root.getElementById?.(id) ?? document.getElementById(id);
|
|
@@ -26,6 +26,8 @@
|
|
|
26
26
|
|
|
27
27
|
import { ADW_CLAMP_DEFAULTS, ADW_CLAMP_SIZE_CLASSES, clampAllocate, normalizeClampSize } from '@gjsify/adwaita-core';
|
|
28
28
|
|
|
29
|
+
import { bindSlottedChildren } from '../slotted-children.js';
|
|
30
|
+
|
|
29
31
|
export class AdwClamp extends HTMLElement {
|
|
30
32
|
private _resize: ResizeObserver | null = null;
|
|
31
33
|
private _mutations: MutationObserver | null = null;
|
|
@@ -35,6 +37,16 @@ export class AdwClamp extends HTMLElement {
|
|
|
35
37
|
}
|
|
36
38
|
|
|
37
39
|
connectedCallback() {
|
|
40
|
+
// `Adw.Clamp:child` is a widget PROPERTY, so an authored `child: …` names the
|
|
41
|
+
// slot by that name (`shared-tree-builder.ts`'s `refuseUnknownSlots`) — and without
|
|
42
|
+
// a declaration for it, the placement was refused sight unseen. There is nowhere to
|
|
43
|
+
// ROUTE it to: every child here is already the placement, clamped in place, so this
|
|
44
|
+
// only enrols the NAME — `.install()` is never called, and nothing is moved. Calling
|
|
45
|
+
// it would be actively wrong: `into: this` would make the routed append a same-
|
|
46
|
+
// parent move, which the DOM still fires as a childList mutation, re-triggering the
|
|
47
|
+
// very MutationObserver that routes it.
|
|
48
|
+
bindSlottedChildren(this, [{ name: 'child', into: this }]);
|
|
49
|
+
|
|
38
50
|
// ResizeObserver delivers an initial observation on observe(), so the
|
|
39
51
|
// first allocation lands before paint without a separate seeding pass.
|
|
40
52
|
this._resize = new ResizeObserver(() => this._allocate());
|
|
@@ -58,6 +58,9 @@
|
|
|
58
58
|
import { ComboState, deriveRowLabels, normalizeComboOptions, parseListModel } from '@gjsify/adwaita-core';
|
|
59
59
|
import type { AdwComboOption, AdwListItemsChanged, AdwListModelInput } from '@gjsify/adwaita-core';
|
|
60
60
|
|
|
61
|
+
import { bindSlottedChildren } from '../slotted-children.js';
|
|
62
|
+
import { stringListSlot } from '../string-list-slot.js';
|
|
63
|
+
|
|
61
64
|
export class AdwComboRow extends HTMLElement {
|
|
62
65
|
private _select!: HTMLSelectElement;
|
|
63
66
|
private _valueEl!: HTMLSpanElement;
|
|
@@ -136,9 +139,6 @@ export class AdwComboRow extends HTMLElement {
|
|
|
136
139
|
// Only when the PROPERTY was not already set, the rule `<adw-data-grid>` and
|
|
137
140
|
// `<gtk-drop-down>` both follow: a model assigned to a detached element must
|
|
138
141
|
// survive being attached, and an absent attribute must not blank it.
|
|
139
|
-
if (this._state.count === 0) this._state.setModel(parseListModel(this.getAttribute('model')));
|
|
140
|
-
this._state.setSelectedIndex(parseInt(this.getAttribute('selected') || '0', 10));
|
|
141
|
-
|
|
142
142
|
const text = document.createElement('div');
|
|
143
143
|
text.className = 'adw-row-text';
|
|
144
144
|
this._titleEl = document.createElement('span');
|
|
@@ -152,7 +152,16 @@ export class AdwComboRow extends HTMLElement {
|
|
|
152
152
|
|
|
153
153
|
this._select = document.createElement('select');
|
|
154
154
|
|
|
155
|
-
|
|
155
|
+
// A `<gtk-string-list slot="model">` child is the list a `.blp` authored (ADR 0072),
|
|
156
|
+
// consumed here BEFORE the attribute seed below, so the seed sees a model and leaves
|
|
157
|
+
// it alone, and before the selection, so an authored index lands on the real list.
|
|
158
|
+
bindSlottedChildren(this, [stringListSlot((model) => this._state.setModel(model))]).install(
|
|
159
|
+
text,
|
|
160
|
+
this._valueEl,
|
|
161
|
+
this._select,
|
|
162
|
+
);
|
|
163
|
+
if (this._state.count === 0) this._state.setModel(parseListModel(this.getAttribute('model')));
|
|
164
|
+
this._state.setSelectedIndex(parseInt(this.getAttribute('selected') || '0', 10));
|
|
156
165
|
|
|
157
166
|
// WHERE the model changed — applied before the selection change below, which is
|
|
158
167
|
// the ordering that lets that subscriber's index write land on the new list.
|
|
@@ -47,6 +47,13 @@ export class AdwHeaderBar extends HTMLElement {
|
|
|
47
47
|
* see the divergence at the top of this file.
|
|
48
48
|
*/
|
|
49
49
|
private _titleEl: HTMLElement | null = null;
|
|
50
|
+
/**
|
|
51
|
+
* The `<adw-navigation-page>` this bar sits in, and the observer that follows its
|
|
52
|
+
* title. `update_title` (adw-header-bar.c:488) asks that page FIRST, which is how a
|
|
53
|
+
* bare `Adw.HeaderBar {}` in a `.blp` page shows "Mailboxes" with no title of its own.
|
|
54
|
+
*/
|
|
55
|
+
private _page: Element | null = null;
|
|
56
|
+
private _pageObserver: MutationObserver | null = null;
|
|
50
57
|
|
|
51
58
|
static get observedAttributes() {
|
|
52
59
|
return ['title', 'subtitle'];
|
|
@@ -69,7 +76,11 @@ export class AdwHeaderBar extends HTMLElement {
|
|
|
69
76
|
}
|
|
70
77
|
|
|
71
78
|
connectedCallback() {
|
|
72
|
-
if (this._initialized)
|
|
79
|
+
if (this._initialized) {
|
|
80
|
+
this._followPage();
|
|
81
|
+
this._renderTitle();
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
73
84
|
this._initialized = true;
|
|
74
85
|
|
|
75
86
|
this._startEl = document.createElement('div');
|
|
@@ -92,10 +103,19 @@ export class AdwHeaderBar extends HTMLElement {
|
|
|
92
103
|
[
|
|
93
104
|
{ name: 'start', into: this._startEl },
|
|
94
105
|
{ name: 'center', into: this._centerEl },
|
|
106
|
+
// The SAME destination under the name GTK writes it at. This section has
|
|
107
|
+
// always documented itself as "the equivalent of Adw.HeaderBar's
|
|
108
|
+
// title-widget"; a tree authored at the property position (a Blueprint
|
|
109
|
+
// `title-widget: …`) carried exactly that name, found no destination for it
|
|
110
|
+
// and stayed a stray sibling — after which this element derived its own
|
|
111
|
+
// title into the centre, so the authored one was gone from the document.
|
|
112
|
+
// `center` stays because it is what this package published and what the
|
|
113
|
+
// gallery's own fences are written in.
|
|
114
|
+
{ name: 'title-widget', into: this._centerEl },
|
|
95
115
|
{ name: 'end', into: this._endEl },
|
|
96
116
|
],
|
|
97
117
|
(_node, slot) => {
|
|
98
|
-
if (slot.
|
|
118
|
+
if ('into' in slot && slot.into === this._centerEl) this._dropDerivedTitle();
|
|
99
119
|
},
|
|
100
120
|
).install(this._startEl, this._centerEl, this._endEl);
|
|
101
121
|
|
|
@@ -108,9 +128,29 @@ export class AdwHeaderBar extends HTMLElement {
|
|
|
108
128
|
this._titleEl.className = 'adw-header-bar-title';
|
|
109
129
|
this._centerEl.appendChild(this._titleEl);
|
|
110
130
|
}
|
|
131
|
+
this._followPage();
|
|
111
132
|
this._renderTitle();
|
|
112
133
|
}
|
|
113
134
|
|
|
135
|
+
disconnectedCallback() {
|
|
136
|
+
this._pageObserver?.disconnect();
|
|
137
|
+
this._pageObserver = null;
|
|
138
|
+
this._page = null;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Find the enclosing navigation page and re-render when its title moves, as GTK does:
|
|
143
|
+
* `root` looks the ancestor up (adw-header-bar.c:586) and a `notify::title` on it re-runs
|
|
144
|
+
* `update_title`. Looked up on every connect, since a bar moved into another page has a
|
|
145
|
+
* new ancestor, which is why C does it in `root` and drops it in `unroot`.
|
|
146
|
+
*/
|
|
147
|
+
private _followPage() {
|
|
148
|
+
this._page = this.closest('adw-navigation-page');
|
|
149
|
+
if (this._page === null) return;
|
|
150
|
+
this._pageObserver ??= new MutationObserver(() => this._renderTitle());
|
|
151
|
+
this._pageObserver.observe(this._page, { attributes: true, attributeFilter: ['title'] });
|
|
152
|
+
}
|
|
153
|
+
|
|
114
154
|
/**
|
|
115
155
|
* `title` used to be read ONCE, in `connectedCallback`, so every later write
|
|
116
156
|
* was a silent no-op — a header bar whose title tracked the open document
|
|
@@ -147,7 +187,10 @@ export class AdwHeaderBar extends HTMLElement {
|
|
|
147
187
|
// empty-string collapse do the work. Removing rather than writing `''`
|
|
148
188
|
// keeps "unset" distinguishable from "set to empty" on the child.
|
|
149
189
|
for (const name of ['title', 'subtitle']) {
|
|
150
|
-
|
|
190
|
+
// The bar's own `title` is this port's declarative divergence (see the header) and
|
|
191
|
+
// wins; without one, the page's title is the first answer `update_title` has.
|
|
192
|
+
const value =
|
|
193
|
+
this.getAttribute(name) ?? (name === 'title' ? (this._page?.getAttribute('title') ?? null) : null);
|
|
151
194
|
if (value === null) this._titleEl.removeAttribute(name);
|
|
152
195
|
else this._titleEl.setAttribute(name, value);
|
|
153
196
|
}
|
|
@@ -55,6 +55,7 @@ import {
|
|
|
55
55
|
domViewSwitcherScheduler,
|
|
56
56
|
readSwitcherPage,
|
|
57
57
|
} from './view-switcher-dom.js';
|
|
58
|
+
import { bindSlottedChildren } from '../slotted-children.js';
|
|
58
59
|
import { attachRovingFocus } from './roving-focus.js';
|
|
59
60
|
|
|
60
61
|
/** A single page. Children of <adw-inline-view-switcher>; consumed at connect time. */
|
|
@@ -62,6 +63,16 @@ export class AdwViewStackPage extends HTMLElement {
|
|
|
62
63
|
static get observedAttributes() {
|
|
63
64
|
return ['name', 'title', 'icon-name', 'badge-number', 'needs-attention', 'use-underline'];
|
|
64
65
|
}
|
|
66
|
+
|
|
67
|
+
connectedCallback(): void {
|
|
68
|
+
// `AdwViewStackPage:child` is a PROPERTY, so a `.blp`'s `child: …` authors
|
|
69
|
+
// `slot="child"`, and the mount refused the name this element never declared. The
|
|
70
|
+
// page's children already ARE its content — the stack or the switcher moves them
|
|
71
|
+
// out when it adopts the page — so this only enrols the name, as `<adw-tab-page>`
|
|
72
|
+
// does. The owner adopts on ITS connect, which runs first, so by now this page may
|
|
73
|
+
// be detached; enrolling is all that is left to do either way.
|
|
74
|
+
bindSlottedChildren(this, [{ name: 'child', into: this }]);
|
|
75
|
+
}
|
|
65
76
|
}
|
|
66
77
|
|
|
67
78
|
export class AdwInlineViewSwitcher extends HTMLElement {
|
|
@@ -54,6 +54,8 @@
|
|
|
54
54
|
import { BACK_BUTTON_FALLBACK_TOOLTIP, NavigationViewState, describeNavigationDiagnostic } from '@gjsify/adwaita-core';
|
|
55
55
|
import type { AdwNavigationPageProps, NavigationDiagnostic, NavigationStackChange } from '@gjsify/adwaita-core';
|
|
56
56
|
|
|
57
|
+
import { bindSlottedChildren } from '../slotted-children.js';
|
|
58
|
+
|
|
57
59
|
/** The page properties the core owns, read off the element's attributes. */
|
|
58
60
|
function readPageProps(page: AdwNavigationPage): AdwNavigationPageProps {
|
|
59
61
|
return { tag: page.tag, title: page.title, canPop: page.canPop };
|
|
@@ -68,6 +70,14 @@ export class AdwNavigationPage extends HTMLElement {
|
|
|
68
70
|
return ['title', 'tag', 'can-pop', 'no-back-button'];
|
|
69
71
|
}
|
|
70
72
|
|
|
73
|
+
connectedCallback(): void {
|
|
74
|
+
// `AdwNavigationPage:child` is a widget PROPERTY, so a `.blp`'s `child: …` authors
|
|
75
|
+
// `slot="child"`. The page's children already ARE its content, so this only enrols
|
|
76
|
+
// the name — nothing is routed, and `.install()` is never called, for the reason
|
|
77
|
+
// `adw-clamp.ts` gives: `into: this` would re-trigger the observer that routes it.
|
|
78
|
+
bindSlottedChildren(this, [{ name: 'child', into: this }]);
|
|
79
|
+
}
|
|
80
|
+
|
|
71
81
|
get title(): string {
|
|
72
82
|
return this.getAttribute('title') ?? '';
|
|
73
83
|
}
|
|
@@ -72,6 +72,28 @@ export class AdwSidebarItem extends HTMLElement {
|
|
|
72
72
|
return ['title', 'subtitle', 'icon-name', 'disabled', 'hidden'];
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
/**
|
|
76
|
+
* `AdwSidebarItem:enabled`, under the GIR name — the `disabled` attribute inverted. The
|
|
77
|
+
* property is what lets an authored `enabled: false` reach this item through the
|
|
78
|
+
* shared-tree builder, which writes a `false` through the element's own property.
|
|
79
|
+
*/
|
|
80
|
+
get enabled(): boolean {
|
|
81
|
+
return !this.hasAttribute('disabled');
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
set enabled(value: boolean) {
|
|
85
|
+
this.toggleAttribute('disabled', !value);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** `AdwSidebarItem:visible` — the `hidden` attribute inverted, for the same reason. */
|
|
89
|
+
get visible(): boolean {
|
|
90
|
+
return !this.hasAttribute('hidden');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
set visible(value: boolean) {
|
|
94
|
+
this.toggleAttribute('hidden', !value);
|
|
95
|
+
}
|
|
96
|
+
|
|
75
97
|
attributeChangedCallback(name: string, _oldValue: string | null, newValue: string | null) {
|
|
76
98
|
const binding = itemBindings.get(this);
|
|
77
99
|
if (!binding) return;
|