@gjsify/adwaita-web 0.45.0 → 0.47.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/README.md +8 -6
- package/dist/adwaita-web.css +96 -96
- package/dist/adwaita-web.css.map +1 -1
- package/lib/types/elements/adw-entry-row.d.ts +2 -2
- package/lib/types/elements/adw-expander-row.d.ts +1 -1
- package/lib/types/elements/adw-split-button.d.ts +1 -1
- package/lib/types/elements/adw-switch-row.d.ts +1 -1
- package/lib/types/elements/adw-wrap-box.d.ts +1 -1
- package/lib/types/elements/{adw-checks.d.ts → checks.d.ts} +9 -2
- package/lib/types/elements/{adw-button.d.ts → gtk-button.d.ts} +1 -1
- package/lib/types/elements/{adw-drop-down.d.ts → gtk-drop-down.d.ts} +6 -6
- package/lib/types/elements/{adw-entry.d.ts → gtk-entry.d.ts} +1 -1
- package/lib/types/elements/{adw-icon.d.ts → gtk-image.d.ts} +2 -2
- package/lib/types/elements/{adw-menu-button.d.ts → gtk-menu-button.d.ts} +13 -4
- package/lib/types/elements/{adw-popover.d.ts → gtk-popover.d.ts} +9 -9
- package/lib/types/elements/{adw-progress-bar.d.ts → gtk-progress-bar.d.ts} +1 -1
- package/lib/types/elements/{adw-switch.d.ts → gtk-switch.d.ts} +2 -2
- package/lib/types/elements/view-switcher-dom.d.ts +4 -4
- package/lib/types/exported-elements.d.ts +10 -0
- package/lib/types/gtk-button.spec.d.ts +1 -0
- package/lib/types/gtk-drop-down.spec.d.ts +1 -0
- package/lib/types/gtk-entry.spec.d.ts +1 -0
- package/lib/types/gtk-image.spec.d.ts +1 -0
- package/lib/types/gtk-menu-button.spec.d.ts +1 -0
- package/lib/types/gtk-progress-bar.spec.d.ts +1 -0
- package/lib/types/gtk-switch.spec.d.ts +1 -0
- package/lib/types/icon-registry.d.ts +2 -2
- package/lib/types/index.d.ts +13 -51
- package/lib/types/namespace/adw.d.ts +44 -0
- package/lib/types/namespace/gtk.d.ts +9 -0
- package/lib/types/styles.generated.d.ts +1 -1
- package/package.json +6 -6
- package/scss/_about_dialog.scss +1 -1
- package/scss/_button.scss +4 -4
- package/scss/_checks.scss +6 -6
- package/scss/_drop_down.scss +3 -3
- package/scss/_entry.scss +3 -3
- package/scss/_expander_row.scss +1 -1
- package/scss/_icon.scss +4 -4
- package/scss/_menu_button.scss +3 -3
- package/scss/_popover.scss +2 -2
- package/scss/_progress_bar.scss +4 -4
- package/scss/_reset.scss +10 -8
- package/scss/_split_button.scss +1 -1
- package/scss/_style_classes.scss +9 -9
- package/scss/_switch.scss +2 -2
- package/scss/_switch_row.scss +1 -1
- package/scss/_toast.scss +1 -1
- package/scss/adwaita-skin.scss +2 -2
- package/src/adw-action-rows.spec.ts +2 -2
- package/src/adw-button-content.spec.ts +1 -1
- package/src/bottom-sheet.spec.ts +2 -2
- package/src/{adw-checks.spec.ts → checks.spec.ts} +11 -11
- package/src/chrome.spec.ts +2 -2
- package/src/connect-lifecycle.spec.ts +10 -7
- package/src/elements/adw-about-dialog.ts +6 -6
- package/src/elements/adw-avatar.ts +4 -4
- package/src/elements/adw-button-content.ts +6 -6
- package/src/elements/adw-button-row.ts +6 -6
- package/src/elements/adw-combo-row.ts +1 -1
- package/src/elements/adw-entry-row.ts +5 -5
- package/src/elements/adw-expander-row.ts +10 -10
- package/src/elements/adw-navigation-view.ts +1 -1
- package/src/elements/adw-password-entry-row.ts +3 -3
- package/src/elements/adw-sidebar.ts +4 -4
- package/src/elements/adw-split-button.ts +14 -14
- package/src/elements/adw-status-page.ts +4 -4
- package/src/elements/adw-switch-row.ts +8 -8
- package/src/elements/adw-tab-view.ts +7 -7
- package/src/elements/adw-toast-overlay.ts +2 -2
- package/src/elements/adw-toggle-group.ts +3 -3
- package/src/elements/adw-view-switcher-bar.ts +3 -3
- package/src/elements/adw-view-switcher.ts +3 -3
- package/src/elements/{adw-checks.ts → checks.ts} +13 -6
- package/src/elements/{adw-button.ts → gtk-button.ts} +6 -6
- package/src/elements/{adw-drop-down.ts → gtk-drop-down.ts} +18 -18
- package/src/elements/{adw-entry.ts → gtk-entry.ts} +4 -4
- package/src/elements/{adw-icon.ts → gtk-image.ts} +5 -5
- package/src/elements/{adw-menu-button.ts → gtk-menu-button.ts} +27 -18
- package/src/elements/{adw-popover.ts → gtk-popover.ts} +18 -18
- package/src/elements/{adw-progress-bar.ts → gtk-progress-bar.ts} +5 -5
- package/src/elements/{adw-switch.ts → gtk-switch.ts} +4 -4
- package/src/elements/view-switcher-dom.ts +5 -5
- package/src/exported-elements.ts +50 -0
- package/src/{adw-button.spec.ts → gtk-button.spec.ts} +6 -6
- package/src/{adw-drop-down.spec.ts → gtk-drop-down.spec.ts} +17 -17
- package/src/{adw-entry.spec.ts → gtk-entry.spec.ts} +8 -8
- package/src/{adw-icon.spec.ts → gtk-image.spec.ts} +21 -21
- package/src/{adw-menu-button.spec.ts → gtk-menu-button.spec.ts} +6 -6
- package/src/{adw-progress-bar.spec.ts → gtk-progress-bar.spec.ts} +12 -12
- package/src/{adw-switch.spec.ts → gtk-switch.spec.ts} +20 -20
- package/src/icon-registry.spec.ts +6 -6
- package/src/icon-registry.ts +6 -6
- package/src/index.ts +64 -63
- package/src/namespace/adw.ts +99 -0
- package/src/namespace/gtk.ts +22 -0
- package/src/preferences-search.ts +1 -1
- package/src/slotted-children.spec.ts +5 -3
- package/src/source-view/adw-source-view.ts +2 -2
- package/src/split-button.spec.ts +5 -5
- package/src/styles.generated.ts +1 -1
- package/src/test.browser.mts +15 -15
- package/lib/types/adw-button.spec.d.ts +0 -1
- package/lib/types/adw-drop-down.spec.d.ts +0 -1
- package/lib/types/adw-entry.spec.d.ts +0 -1
- package/lib/types/adw-icon.spec.d.ts +0 -1
- package/lib/types/adw-menu-button.spec.d.ts +0 -1
- package/lib/types/adw-progress-bar.spec.d.ts +0 -1
- package/lib/types/adw-switch.spec.d.ts +0 -1
- /package/lib/types/{adw-checks.spec.d.ts → checks.spec.d.ts} +0 -0
|
@@ -54,13 +54,13 @@ import {
|
|
|
54
54
|
domViewSwitcherScheduler,
|
|
55
55
|
readSwitcherPage,
|
|
56
56
|
} from './view-switcher-dom.js';
|
|
57
|
-
import { type
|
|
57
|
+
import { type GtkImage, createGtkImage } from './gtk-image.js';
|
|
58
58
|
import { attachRovingFocus } from './roving-focus.js';
|
|
59
59
|
|
|
60
60
|
/** The DOM nodes one page owns — the switcher paints the derived model onto them. */
|
|
61
61
|
interface PageNodes {
|
|
62
62
|
button: HTMLButtonElement;
|
|
63
|
-
icon:
|
|
63
|
+
icon: GtkImage;
|
|
64
64
|
label: HTMLSpanElement;
|
|
65
65
|
indicator: HTMLSpanElement;
|
|
66
66
|
body: HTMLDivElement;
|
|
@@ -271,7 +271,7 @@ export class AdwViewSwitcher extends HTMLElement {
|
|
|
271
271
|
|
|
272
272
|
// Both children always exist, as in AdwViewSwitcherButton's template — the icon
|
|
273
273
|
// carries `image-missing` rather than disappearing.
|
|
274
|
-
const icon =
|
|
274
|
+
const icon = createGtkImage(null, 'adw-view-switcher-icon');
|
|
275
275
|
|
|
276
276
|
const label = document.createElement('span');
|
|
277
277
|
label.className = 'adw-view-switcher-label';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// <
|
|
1
|
+
// <gtk-check-button> and <adw-radio> — the two form-control primitives, in ONE module,
|
|
2
2
|
// for the same reason `scss/_checks.scss` is one partial: upstream merges them
|
|
3
3
|
// (`refs/libadwaita/src/stylesheet/widgets/_checks.scss` has no `_radio.scss` beside
|
|
4
4
|
// it), and everything but the corner radius, the glyph and the group is shared.
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
// disabled — boolean; the control does not respond and is dimmed.
|
|
36
36
|
// label — the text beside the indicator. When absent, the element's
|
|
37
37
|
// existing child nodes become the label (so a server-rendered
|
|
38
|
-
// `<
|
|
38
|
+
// `<gtk-check-button>Enable</gtk-check-button>` upgrades intact).
|
|
39
39
|
// Attributes (<adw-radio> only):
|
|
40
40
|
// name — the group. Exclusivity is scoped to it, exactly as `name`
|
|
41
41
|
// scopes `<input type="radio">` — including the empty case:
|
|
@@ -68,7 +68,14 @@ import { RadioGroupState, resolveCheckState } from '@gjsify/adwaita-core';
|
|
|
68
68
|
*/
|
|
69
69
|
const radioGroups = new RadioGroupState();
|
|
70
70
|
|
|
71
|
-
/**
|
|
71
|
+
/**
|
|
72
|
+
* The markup + state shared by `<gtk-check-button>` and `<adw-radio>`.
|
|
73
|
+
*
|
|
74
|
+
* It keeps the `Adw` prefix while the element above it does not, and the split is the
|
|
75
|
+
* same one the stylesheet makes: this class builds `.adw-check-input`,
|
|
76
|
+
* `.adw-check-indicator` and `.adw-check-label`, which are the Adwaita SKIN, and the
|
|
77
|
+
* skin is not what the tag names. It is also not a widget — nothing registers it.
|
|
78
|
+
*/
|
|
72
79
|
abstract class AdwCheckBase extends HTMLElement {
|
|
73
80
|
protected _input!: HTMLInputElement;
|
|
74
81
|
protected _indicator!: HTMLSpanElement;
|
|
@@ -124,7 +131,7 @@ abstract class AdwCheckBase extends HTMLElement {
|
|
|
124
131
|
this._initialized = true;
|
|
125
132
|
|
|
126
133
|
// Captured BEFORE the element takes over its own children, so a server-rendered
|
|
127
|
-
// `<
|
|
134
|
+
// `<gtk-check-button>Enable</gtk-check-button>` keeps its text.
|
|
128
135
|
const authored = [...this.childNodes];
|
|
129
136
|
|
|
130
137
|
this._input = document.createElement('input');
|
|
@@ -193,7 +200,7 @@ abstract class AdwCheckBase extends HTMLElement {
|
|
|
193
200
|
}
|
|
194
201
|
|
|
195
202
|
/** The Adwaita checkbox — `checkbutton > check` as a custom element. */
|
|
196
|
-
export class
|
|
203
|
+
export class GtkCheckButton extends AdwCheckBase {
|
|
197
204
|
static get observedAttributes() {
|
|
198
205
|
return ['checked', 'indeterminate', 'disabled', 'label'];
|
|
199
206
|
}
|
|
@@ -282,5 +289,5 @@ export class AdwRadio extends AdwCheckBase {
|
|
|
282
289
|
}
|
|
283
290
|
}
|
|
284
291
|
|
|
285
|
-
customElements.define('
|
|
292
|
+
customElements.define('gtk-check-button', GtkCheckButton);
|
|
286
293
|
customElements.define('adw-radio', AdwRadio);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// <
|
|
1
|
+
// <gtk-button> — Adwaita button.
|
|
2
2
|
// Attributes: icon (symbolic name, e.g. "go-previous" / "view-refresh"),
|
|
3
3
|
// label, tooltip, disabled, and the boolean variant flags
|
|
4
4
|
// flat / suggested / destructive / circular / pill.
|
|
@@ -7,16 +7,16 @@
|
|
|
7
7
|
// Reference: refs/libadwaita/src/stylesheet/widgets/_buttons.scss
|
|
8
8
|
// Copyright (c) GNOME contributors (libadwaita). LGPLv2.1+.
|
|
9
9
|
// Modifications: Implemented as a Web Component for @gjsify/adwaita-web; the
|
|
10
|
-
// icon node is <
|
|
10
|
+
// icon node is <gtk-image>.
|
|
11
11
|
|
|
12
12
|
import { buttonStyleClasses } from '@gjsify/adwaita-core';
|
|
13
13
|
|
|
14
|
-
import {
|
|
14
|
+
import { createGtkImage } from './gtk-image.js';
|
|
15
15
|
|
|
16
16
|
/** The boolean attributes that select a style class; the mapping lives in the core. */
|
|
17
17
|
const STYLE_ATTRIBUTES = ['flat', 'suggested', 'destructive', 'circular', 'pill'] as const;
|
|
18
18
|
|
|
19
|
-
export class
|
|
19
|
+
export class GtkButton extends HTMLElement {
|
|
20
20
|
private _button!: HTMLButtonElement;
|
|
21
21
|
private _label = '';
|
|
22
22
|
private _initialized = false;
|
|
@@ -57,7 +57,7 @@ export class AdwButton extends HTMLElement {
|
|
|
57
57
|
if (icon && !label) btn.classList.add('icon-only');
|
|
58
58
|
|
|
59
59
|
btn.replaceChildren();
|
|
60
|
-
if (icon) btn.appendChild(
|
|
60
|
+
if (icon) btn.appendChild(createGtkImage(icon));
|
|
61
61
|
if (label) btn.appendChild(document.createTextNode(label));
|
|
62
62
|
|
|
63
63
|
const tooltip = this.getAttribute('tooltip');
|
|
@@ -74,4 +74,4 @@ export class AdwButton extends HTMLElement {
|
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
customElements.define('
|
|
77
|
+
customElements.define('gtk-button', GtkButton);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// <
|
|
1
|
+
// <gtk-drop-down> — A standalone dropdown selector: the web counterpart of
|
|
2
2
|
// Gtk.DropDown. It is a flat Adwaita button showing the current selection (its
|
|
3
3
|
// label + a `pan-down` chevron) that opens an Adwaita popover list of options;
|
|
4
4
|
// picking one updates the selection. This is the toolbar/inline control twin of
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
// Escape and supports arrow-key navigation, Home/End, Enter to pick, and
|
|
13
13
|
// type-ahead (typing jumps to the next matching option).
|
|
14
14
|
//
|
|
15
|
-
// The popover is `<
|
|
15
|
+
// The popover is `<gtk-popover>` and the dismissal/keyboard machine is
|
|
16
16
|
// `@gjsify/adwaita-core`'s (ADR 0004). So is the SELECTION: the options list, the
|
|
17
17
|
// index↔value mapping, the bounds guards and the programmatic-vs-interactive split are
|
|
18
18
|
// {@link ComboState}, the same state machine `<adw-combo-row>` composes — so both
|
|
@@ -47,31 +47,31 @@
|
|
|
47
47
|
// Modifications: Implemented as a Web Component for @gjsify/adwaita-web.
|
|
48
48
|
|
|
49
49
|
// SIDE-EFFECT import, deliberately separate from the type import below: it is
|
|
50
|
-
// what guarantees `
|
|
51
|
-
// `customElements.define` can upgrade a server-rendered `<
|
|
52
|
-
// build one. A combined `import {
|
|
50
|
+
// what guarantees `gtk-popover` is defined before this module's own
|
|
51
|
+
// `customElements.define` can upgrade a server-rendered `<gtk-drop-down>` and
|
|
52
|
+
// build one. A combined `import { GtkPopover }` would NOT do it — the binding is
|
|
53
53
|
// only ever used in type position here, and this package compiles without
|
|
54
54
|
// `verbatimModuleSyntax`, so TypeScript would elide the whole statement and take
|
|
55
55
|
// the registration with it.
|
|
56
|
-
import './
|
|
57
|
-
import type {
|
|
56
|
+
import './gtk-popover.js';
|
|
57
|
+
import type { GtkPopover } from './gtk-popover.js';
|
|
58
58
|
|
|
59
59
|
import { ComboState, normalizeComboOptions } from '@gjsify/adwaita-core';
|
|
60
60
|
import type { AdwComboOption, AdwComboOptionInput } from '@gjsify/adwaita-core';
|
|
61
61
|
|
|
62
|
-
import {
|
|
62
|
+
import { createGtkImage } from './gtk-image.js';
|
|
63
63
|
|
|
64
64
|
/**
|
|
65
65
|
* A dropdown option — `value` is the stable id, `label` the shown text. An alias of
|
|
66
66
|
* core's {@link AdwComboOption}, since the drop-down and the combo row are the same
|
|
67
67
|
* selection model behind two chromes; still exported so the published name keeps working.
|
|
68
68
|
*/
|
|
69
|
-
export type
|
|
69
|
+
export type GtkDropDownOption = AdwComboOption;
|
|
70
70
|
|
|
71
|
-
export class
|
|
71
|
+
export class GtkDropDown extends HTMLElement {
|
|
72
72
|
private _buttonEl!: HTMLButtonElement;
|
|
73
73
|
private _labelEl!: HTMLSpanElement;
|
|
74
|
-
private _popoverEl!:
|
|
74
|
+
private _popoverEl!: GtkPopover;
|
|
75
75
|
private _listEl!: HTMLDivElement;
|
|
76
76
|
private _searchEl: HTMLInputElement | null = null;
|
|
77
77
|
private _itemButtons: HTMLButtonElement[] = [];
|
|
@@ -86,7 +86,7 @@ export class AdwDropDown extends HTMLElement {
|
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
/** The options list. Setting it rebuilds the popover and clamps the selection. */
|
|
89
|
-
get options():
|
|
89
|
+
get options(): GtkDropDownOption[] {
|
|
90
90
|
return this._state.options;
|
|
91
91
|
}
|
|
92
92
|
|
|
@@ -97,7 +97,7 @@ export class AdwDropDown extends HTMLElement {
|
|
|
97
97
|
if (this._initialized) this._renderAll();
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
-
get items():
|
|
100
|
+
get items(): GtkDropDownOption[] {
|
|
101
101
|
return this._state.options;
|
|
102
102
|
}
|
|
103
103
|
|
|
@@ -124,7 +124,7 @@ export class AdwDropDown extends HTMLElement {
|
|
|
124
124
|
if (index >= 0) this._selectIndex(index);
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
-
get selectedItem():
|
|
127
|
+
get selectedItem(): GtkDropDownOption | null {
|
|
128
128
|
return this._state.options[this._state.selectedIndex] ?? null;
|
|
129
129
|
}
|
|
130
130
|
|
|
@@ -156,7 +156,7 @@ export class AdwDropDown extends HTMLElement {
|
|
|
156
156
|
this._labelEl = document.createElement('span');
|
|
157
157
|
this._labelEl.className = 'adw-drop-down-label';
|
|
158
158
|
|
|
159
|
-
this._buttonEl.append(this._labelEl,
|
|
159
|
+
this._buttonEl.append(this._labelEl, createGtkImage('go-down', 'adw-drop-down-arrow'));
|
|
160
160
|
this._buttonEl.addEventListener('click', () => {
|
|
161
161
|
if (this.hasAttribute('disabled')) return;
|
|
162
162
|
if (!this._popoverEl.open && this._state.count === 0) return;
|
|
@@ -164,7 +164,7 @@ export class AdwDropDown extends HTMLElement {
|
|
|
164
164
|
});
|
|
165
165
|
this._buttonEl.addEventListener('keydown', (event) => this._onButtonKeyDown(event));
|
|
166
166
|
|
|
167
|
-
this._popoverEl = document.createElement('
|
|
167
|
+
this._popoverEl = document.createElement('gtk-popover') as GtkPopover;
|
|
168
168
|
this._popoverEl.classList.add('adw-drop-down-popover');
|
|
169
169
|
this._popoverEl.setAttribute('role', 'listbox');
|
|
170
170
|
// A dropdown's popover is a `popover.menu` too — `dropdown { popover.menu { … } }`
|
|
@@ -208,7 +208,7 @@ export class AdwDropDown extends HTMLElement {
|
|
|
208
208
|
}
|
|
209
209
|
}
|
|
210
210
|
|
|
211
|
-
private _parseOptionsAttr():
|
|
211
|
+
private _parseOptionsAttr(): GtkDropDownOption[] {
|
|
212
212
|
const raw = this.getAttribute('options') ?? this.getAttribute('items');
|
|
213
213
|
if (!raw) return [];
|
|
214
214
|
try {
|
|
@@ -378,4 +378,4 @@ export class AdwDropDown extends HTMLElement {
|
|
|
378
378
|
}
|
|
379
379
|
}
|
|
380
380
|
|
|
381
|
-
customElements.define('
|
|
381
|
+
customElements.define('gtk-drop-down', GtkDropDown);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// <
|
|
1
|
+
// <gtk-entry> — Adwaita single-line text entry (e.g. a browser URL bar).
|
|
2
2
|
// Attributes: value, placeholder, type, disabled, maxlength.
|
|
3
3
|
// Properties: value (get/set, proxies the inner input), maxLength, textLength.
|
|
4
4
|
// Events: native `input` bubbles from the inner input; `activate` (CustomEvent)
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
//
|
|
7
7
|
// The character arithmetic is HEADLESS and lives in `@gjsify/adwaita-core`
|
|
8
8
|
// (ADR 0004): `entryTextLength` counts CODE POINTS and `clampEntryText` truncates
|
|
9
|
-
// on them. `@gjsify/adwaita-nativescript`'s
|
|
9
|
+
// on them. `@gjsify/adwaita-nativescript`'s GtkEntry has composed the same two
|
|
10
10
|
// since it shipped; this element counted nothing at all, so the same consumer
|
|
11
11
|
// got a max length on one renderer and none on the other — and the native
|
|
12
12
|
// `maxlength` attribute would not have closed the gap, because the browser
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
|
|
22
22
|
import { ENTRY_ROW_MAX_LENGTH_LIMIT, clampEntryText, entryTextLength } from '@gjsify/adwaita-core';
|
|
23
23
|
|
|
24
|
-
export class
|
|
24
|
+
export class GtkEntry extends HTMLElement {
|
|
25
25
|
private _input!: HTMLInputElement;
|
|
26
26
|
private _initialized = false;
|
|
27
27
|
private _maxLength = 0;
|
|
@@ -103,4 +103,4 @@ export class AdwEntry extends HTMLElement {
|
|
|
103
103
|
}
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
-
customElements.define('
|
|
106
|
+
customElements.define('gtk-entry', GtkEntry);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// <
|
|
1
|
+
// <gtk-image> — the symbolic icon node as an element. Every icon in this package is a
|
|
2
2
|
// CSS-masked box whose glyph comes from a generated `.adw-icon--<name>` class and
|
|
3
3
|
// whose colour is `currentColor`, so it themes with whatever contains it. The name
|
|
4
4
|
// derivation and its guard live once in `@gjsify/adwaita-core`'s
|
|
@@ -46,7 +46,7 @@ export const MASK_CLASS_PREFIX = 'adw-icon--';
|
|
|
46
46
|
*/
|
|
47
47
|
export const MISSING_ICON_NAME = 'image-missing';
|
|
48
48
|
|
|
49
|
-
export class
|
|
49
|
+
export class GtkImage extends HTMLElement {
|
|
50
50
|
static get observedAttributes() {
|
|
51
51
|
return ['icon-name', 'size'];
|
|
52
52
|
}
|
|
@@ -141,11 +141,11 @@ export class AdwIcon extends HTMLElement {
|
|
|
141
141
|
* Build one icon node. `extraClasses` are the caller's own positioning/context
|
|
142
142
|
* classes, added alongside the managed `.adw-icon` + mask class, not replacing them.
|
|
143
143
|
*/
|
|
144
|
-
export function
|
|
145
|
-
const icon = document.createElement('
|
|
144
|
+
export function createGtkImage(iconName: string | null, ...extraClasses: string[]): GtkImage {
|
|
145
|
+
const icon = document.createElement('gtk-image') as GtkImage;
|
|
146
146
|
if (iconName !== null) icon.setAttribute('icon-name', iconName);
|
|
147
147
|
if (extraClasses.length > 0) icon.classList.add(...extraClasses);
|
|
148
148
|
return icon;
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
-
customElements.define('
|
|
151
|
+
customElements.define('gtk-image', GtkImage);
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
// <
|
|
1
|
+
// <gtk-menu-button> — the web counterpart of Gtk.MenuButton, which libadwaita styles
|
|
2
2
|
// but never subclassed: by default the flat `open-menu-symbolic` app-menu button used
|
|
3
3
|
// at the end of a header bar. Clicking it toggles an Adwaita-styled popover under the
|
|
4
4
|
// button, dismissed on an outside click or Escape, with arrow-key navigation.
|
|
5
5
|
//
|
|
6
|
-
// The popover is `<
|
|
6
|
+
// The popover is `<gtk-popover>` and the dismissal/keyboard machine is
|
|
7
7
|
// `@gjsify/adwaita-core`'s (ADR 0004); this element keeps only what is a menu button:
|
|
8
|
-
// the icon, the item model, the title. Icons go through `<
|
|
8
|
+
// the icon, the item model, the title. Icons go through `<gtk-image>`, whose
|
|
9
9
|
// `normalizeIconName` guard is what keeps a multi-token `icon-name="a b"` from
|
|
10
10
|
// shipping a stray CSS class — for the button icon and for every menu entry's.
|
|
11
11
|
//
|
|
@@ -30,19 +30,28 @@ import {
|
|
|
30
30
|
import type { AdwMenuEntry, SplitButtonDirection } from '@gjsify/adwaita-core';
|
|
31
31
|
|
|
32
32
|
// SIDE-EFFECT import, deliberately separate from the type import below: it guarantees
|
|
33
|
-
// `
|
|
34
|
-
// server-rendered `<
|
|
35
|
-
// `import {
|
|
33
|
+
// `gtk-popover` is defined before this module's `customElements.define` can upgrade a
|
|
34
|
+
// server-rendered `<gtk-menu-button>` and build one. A combined
|
|
35
|
+
// `import { GtkPopover }` would NOT do it — the binding is only used in type position,
|
|
36
36
|
// and this package compiles without `verbatimModuleSyntax`, so TypeScript would elide
|
|
37
37
|
// the statement and take the registration with it.
|
|
38
|
-
import './
|
|
39
|
-
import type {
|
|
38
|
+
import './gtk-popover.js';
|
|
39
|
+
import type { GtkPopover } from './gtk-popover.js';
|
|
40
40
|
|
|
41
|
-
import {
|
|
41
|
+
import { createGtkImage } from './gtk-image.js';
|
|
42
42
|
|
|
43
43
|
/**
|
|
44
|
-
* A menu entry — `@gjsify/adwaita-core`'s {@link AdwMenuEntry}
|
|
45
|
-
*
|
|
44
|
+
* A menu entry — `@gjsify/adwaita-core`'s {@link AdwMenuEntry} under the name both
|
|
45
|
+
* renderers export it as.
|
|
46
|
+
*
|
|
47
|
+
* IT KEEPS THE `Adw` PREFIX WHILE THE ELEMENT ABOVE IT DOES NOT, and the reason is not
|
|
48
|
+
* "the name it has always used" — ADR 0034 clause 1 just moved every name in this file
|
|
49
|
+
* that had one. It is that this is not a WIDGET name: clause 1 is about the tag, and
|
|
50
|
+
* this is the descriptor a consumer writes menu entries with. `@gjsify/adwaita-
|
|
51
|
+
* nativescript` exports `AdwMenuItem` for the same type, and that port deliberately
|
|
52
|
+
* keeps `Adw` throughout, so renaming this half would give one object two spellings —
|
|
53
|
+
* which is the thing the ADR is removing, not applying. (`GtkDropDownOption` next door
|
|
54
|
+
* DID move: its NativeScript twin exports no such name, so there was nothing to split.)
|
|
46
55
|
*
|
|
47
56
|
* It used to be a SECOND declaration of the same shape, and the NativeScript menu
|
|
48
57
|
* button declared a THIRD that was missing `icon` entirely. One type, so a consumer
|
|
@@ -65,9 +74,9 @@ const POPOVER_POSITIONS = {
|
|
|
65
74
|
right: 'end',
|
|
66
75
|
} as const;
|
|
67
76
|
|
|
68
|
-
export class
|
|
77
|
+
export class GtkMenuButton extends HTMLElement {
|
|
69
78
|
private _buttonEl!: HTMLButtonElement;
|
|
70
|
-
private _popoverEl!:
|
|
79
|
+
private _popoverEl!: GtkPopover;
|
|
71
80
|
private _items: AdwMenuItem[] = [];
|
|
72
81
|
private _menuTitle = '';
|
|
73
82
|
private _initialized = false;
|
|
@@ -124,7 +133,7 @@ export class AdwMenuButton extends HTMLElement {
|
|
|
124
133
|
this._buttonEl.setAttribute('aria-haspopup', 'menu');
|
|
125
134
|
this._buttonEl.setAttribute('aria-expanded', 'false');
|
|
126
135
|
|
|
127
|
-
this._popoverEl = document.createElement('
|
|
136
|
+
this._popoverEl = document.createElement('gtk-popover') as GtkPopover;
|
|
128
137
|
this._popoverEl.classList.add('adw-menu-button-popover');
|
|
129
138
|
this._popoverEl.setAttribute('role', 'menu');
|
|
130
139
|
// `gtk_menu_button_set_menu_model` builds a GtkPopoverMenu, whose CSS
|
|
@@ -190,7 +199,7 @@ export class AdwMenuButton extends HTMLElement {
|
|
|
190
199
|
|
|
191
200
|
this._buttonEl.replaceChildren();
|
|
192
201
|
this._buttonEl.appendChild(
|
|
193
|
-
|
|
202
|
+
createGtkImage(this.getAttribute('icon-name') ?? 'open-menu', 'adw-menu-button-icon'),
|
|
194
203
|
);
|
|
195
204
|
this._buttonEl.setAttribute('aria-label', this.getAttribute('menu-title') || 'Menu');
|
|
196
205
|
|
|
@@ -219,8 +228,8 @@ export class AdwMenuButton extends HTMLElement {
|
|
|
219
228
|
// An entry that asked for NO icon gets no icon node — the emptiness of the
|
|
220
229
|
// declared name, not whether it resolved. Testing `resolvedIconName` dropped
|
|
221
230
|
// the node for a name that was given and merely undrawable, which is the one
|
|
222
|
-
// case a reader needs to SEE; `<
|
|
223
|
-
const entryIcon =
|
|
231
|
+
// case a reader needs to SEE; `<gtk-image>` draws `image-missing` there.
|
|
232
|
+
const entryIcon = createGtkImage(entry.icon ?? null, 'adw-menu-button-item-icon');
|
|
224
233
|
if (stringIsNotEmpty(entry.icon)) item.appendChild(entryIcon);
|
|
225
234
|
const labelEl = document.createElement('span');
|
|
226
235
|
labelEl.className = 'adw-menu-button-item-label';
|
|
@@ -245,4 +254,4 @@ export class AdwMenuButton extends HTMLElement {
|
|
|
245
254
|
}
|
|
246
255
|
}
|
|
247
256
|
|
|
248
|
-
customElements.define('
|
|
257
|
+
customElements.define('gtk-menu-button', GtkMenuButton);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
// <
|
|
1
|
+
// <gtk-popover> — the elevated surface that pops up next to an anchor, the web
|
|
2
2
|
// counterpart of GtkPopover as libadwaita styles it. It is the ONE popover in this
|
|
3
|
-
// package: `<
|
|
3
|
+
// package: `<gtk-menu-button>`, `<gtk-drop-down>` and `<adw-split-button>` all use it
|
|
4
4
|
// rather than building their own, which is what keeps the 15px radius, the
|
|
5
5
|
// three-layer shadow, Escape dismissal and the wrap arithmetic in one place.
|
|
6
6
|
//
|
|
@@ -51,22 +51,22 @@
|
|
|
51
51
|
import { PopoverState, resolvePopoverKey } from '@gjsify/adwaita-core';
|
|
52
52
|
|
|
53
53
|
/** Which side of the anchor the surface sits on. */
|
|
54
|
-
export type
|
|
54
|
+
export type GtkPopoverPosition = 'bottom' | 'top' | 'start' | 'end';
|
|
55
55
|
|
|
56
56
|
/** Which edge of the anchor the surface lines up with. */
|
|
57
|
-
export type
|
|
57
|
+
export type GtkPopoverAlign = 'start' | 'end';
|
|
58
58
|
|
|
59
59
|
/** The ARIA role of the surface — it decides the row role consumers give their items. */
|
|
60
|
-
export type
|
|
60
|
+
export type GtkPopoverRole = 'menu' | 'listbox';
|
|
61
61
|
|
|
62
|
-
const POSITIONS: readonly
|
|
63
|
-
const ALIGNS: readonly
|
|
64
|
-
const ROLES: readonly
|
|
62
|
+
const POSITIONS: readonly GtkPopoverPosition[] = ['bottom', 'top', 'start', 'end'];
|
|
63
|
+
const ALIGNS: readonly GtkPopoverAlign[] = ['start', 'end'];
|
|
64
|
+
const ROLES: readonly GtkPopoverRole[] = ['menu', 'listbox'];
|
|
65
65
|
|
|
66
66
|
/** The row selector keyboard navigation walks. */
|
|
67
67
|
const ITEM_SELECTOR = '.adw-popover-item';
|
|
68
68
|
|
|
69
|
-
export class
|
|
69
|
+
export class GtkPopover extends HTMLElement {
|
|
70
70
|
private readonly _state = new PopoverState();
|
|
71
71
|
private _anchor: HTMLElement | null = null;
|
|
72
72
|
private _initialized = false;
|
|
@@ -116,27 +116,27 @@ export class AdwPopover extends HTMLElement {
|
|
|
116
116
|
this._anchor = value;
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
-
get position():
|
|
119
|
+
get position(): GtkPopoverPosition {
|
|
120
120
|
const value = this.getAttribute('position');
|
|
121
|
-
return POSITIONS.includes(value as
|
|
121
|
+
return POSITIONS.includes(value as GtkPopoverPosition) ? (value as GtkPopoverPosition) : 'bottom';
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
-
set position(value:
|
|
124
|
+
set position(value: GtkPopoverPosition) {
|
|
125
125
|
this.setAttribute('position', value);
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
-
get align():
|
|
128
|
+
get align(): GtkPopoverAlign {
|
|
129
129
|
const value = this.getAttribute('align');
|
|
130
|
-
return ALIGNS.includes(value as
|
|
130
|
+
return ALIGNS.includes(value as GtkPopoverAlign) ? (value as GtkPopoverAlign) : 'start';
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
-
set align(value:
|
|
133
|
+
set align(value: GtkPopoverAlign) {
|
|
134
134
|
this.setAttribute('align', value);
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
-
get popoverRole():
|
|
137
|
+
get popoverRole(): GtkPopoverRole {
|
|
138
138
|
const value = this.getAttribute('role');
|
|
139
|
-
return ROLES.includes(value as
|
|
139
|
+
return ROLES.includes(value as GtkPopoverRole) ? (value as GtkPopoverRole) : 'menu';
|
|
140
140
|
}
|
|
141
141
|
|
|
142
142
|
/** The navigable rows, in DOM order. Hidden rows are skipped — a filtered list navigates what it shows. */
|
|
@@ -275,4 +275,4 @@ export class AdwPopover extends HTMLElement {
|
|
|
275
275
|
}
|
|
276
276
|
}
|
|
277
277
|
|
|
278
|
-
customElements.define('
|
|
278
|
+
customElements.define('gtk-popover', GtkPopover);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// <
|
|
1
|
+
// <gtk-progress-bar> — the DETERMINATE progress indicator, and the partner to
|
|
2
2
|
// <adw-spinner>, which only ever covers "busy, no idea how long".
|
|
3
3
|
//
|
|
4
4
|
// NO CORE STATE MACHINE: the whole behaviour is `CLAMP(fraction, 0, 1)` plus a pulsing
|
|
@@ -9,9 +9,9 @@
|
|
|
9
9
|
// `gtk_progress_bar_pulse()`'s step semantics (the `pulse-step` default, how the block
|
|
10
10
|
// reflects at the ends, whether it resets `fraction`) are NOT reproduced: GtkProgressBar
|
|
11
11
|
// is a GTK widget, `refs/gtk` is an uninitialized submodule here and libadwaita vendors
|
|
12
|
-
// no `
|
|
12
|
+
// no `gtk-progress-bar.c`, so none of it is verifiable. What ships instead is the
|
|
13
13
|
// vendored port's web idiom — a CSS keyframe animation that runs while `pulsing` is set
|
|
14
|
-
// — so {@link
|
|
14
|
+
// — so {@link GtkProgressBar.pulse} ENTERS that state and is idempotent afterwards. The
|
|
15
15
|
// default `show-text` label `NN%` is likewise this port's, not GTK's format string.
|
|
16
16
|
//
|
|
17
17
|
// `osd` is libadwaita's `.osd` STYLE CLASS: a 2px troughless bar for under a header bar.
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
|
|
30
30
|
import { glibClamp } from '@gjsify/adwaita-core';
|
|
31
31
|
|
|
32
|
-
export class
|
|
32
|
+
export class GtkProgressBar extends HTMLElement {
|
|
33
33
|
private _trough!: HTMLSpanElement;
|
|
34
34
|
private _progress!: HTMLSpanElement;
|
|
35
35
|
private _text!: HTMLSpanElement;
|
|
@@ -166,4 +166,4 @@ export class AdwProgressBar extends HTMLElement {
|
|
|
166
166
|
}
|
|
167
167
|
}
|
|
168
168
|
|
|
169
|
-
customElements.define('
|
|
169
|
+
customElements.define('gtk-progress-bar', GtkProgressBar);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// <
|
|
1
|
+
// <gtk-switch> — the Adwaita toggle, as an element. A 44×24 track with a hidden
|
|
2
2
|
// checkbox behind it and a knob that slides on `active`.
|
|
3
3
|
//
|
|
4
4
|
// The styles are the UNSCOPED `scss/_switch.scss`, so `.adw-switch` exists outside a
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
// Copyright (c) 2025 csm (adwaita-web). MIT License.
|
|
30
30
|
// Modifications: Implemented as a Web Component for @gjsify/adwaita-web.
|
|
31
31
|
|
|
32
|
-
export class
|
|
32
|
+
export class GtkSwitch extends HTMLElement {
|
|
33
33
|
private _input!: HTMLInputElement;
|
|
34
34
|
private _slider!: HTMLSpanElement;
|
|
35
35
|
private _initialized = false;
|
|
@@ -44,7 +44,7 @@ export class AdwSwitch extends HTMLElement {
|
|
|
44
44
|
* `adw_switch_row_init` does `gtk_widget_set_can_focus (self->slider, FALSE)`
|
|
45
45
|
* (adw-switch-row.c:159) and makes the ROW activatable instead, so a switch row is ONE
|
|
46
46
|
* tab stop announced as its own title — not a bare checkbox followed by a label that
|
|
47
|
-
* cannot be reached. A standalone `<
|
|
47
|
+
* cannot be reached. A standalone `<gtk-switch>` keeps its checkbox focusable.
|
|
48
48
|
*/
|
|
49
49
|
get unfocusable(): boolean {
|
|
50
50
|
return this.hasAttribute('unfocusable');
|
|
@@ -125,4 +125,4 @@ export class AdwSwitch extends HTMLElement {
|
|
|
125
125
|
}
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
-
customElements.define('
|
|
128
|
+
customElements.define('gtk-switch', GtkSwitch);
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
import { viewSwitcherIconName } from '@gjsify/adwaita-core';
|
|
21
21
|
import type { AdwViewSwitcherPageInit, ViewSwitcherScheduler, ViewSwitcherTimerHandle } from '@gjsify/adwaita-core';
|
|
22
22
|
|
|
23
|
-
import { type
|
|
23
|
+
import { type GtkImage, createGtkImage } from './gtk-image.js';
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
26
|
* The browser's timer behind the core's scheduler seam. One shared instance rather than
|
|
@@ -41,16 +41,16 @@ export const domViewSwitcherScheduler: ViewSwitcherScheduler = {
|
|
|
41
41
|
*
|
|
42
42
|
* The core resolves NULL/empty to `image-missing` (which the stylesheet carries
|
|
43
43
|
* a real glyph for); the `-symbolic` strip and the CSS-token guard are
|
|
44
|
-
* `normalizeIconName`'s, applied by `<
|
|
44
|
+
* `normalizeIconName`'s, applied by `<gtk-image>` — that strip is this package's
|
|
45
45
|
* own mask-class convention and has no counterpart in C, where the name reaches
|
|
46
46
|
* `GtkImage` untouched.
|
|
47
47
|
*/
|
|
48
|
-
export function createSwitcherIcon(iconName: string, extra: string):
|
|
49
|
-
return
|
|
48
|
+
export function createSwitcherIcon(iconName: string, extra: string): GtkImage {
|
|
49
|
+
return createGtkImage(viewSwitcherIconName(iconName), extra);
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
/** Repaint an existing switcher icon from a page's declared name. */
|
|
53
|
-
export function applySwitcherIcon(icon:
|
|
53
|
+
export function applySwitcherIcon(icon: GtkImage, iconName: string): void {
|
|
54
54
|
icon.iconName = viewSwitcherIconName(iconName);
|
|
55
55
|
}
|
|
56
56
|
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// Every custom-element constructor this package's barrel exports, however it exports it.
|
|
2
|
+
//
|
|
3
|
+
// THE INCIDENT THIS EXISTS TO PREVENT, in the shape it was caught. Two browser drivers
|
|
4
|
+
// derive their element set from the package rather than from a list — `Object.values` of
|
|
5
|
+
// the module namespace, kept to the functions whose prototype is an `HTMLElement`. That
|
|
6
|
+
// read the widget classes because the barrel re-exported every one of them flat. ADR 0034
|
|
7
|
+
// § Amendment 6 removed those re-exports: the same scan would have gone on passing while
|
|
8
|
+
// its subject shrank to the handful of web-only classes that keep a flat name. Measured
|
|
9
|
+
// on chromium: the narrow scan drops 187 of the package's 4633 browser assertions, and
|
|
10
|
+
// the only thing that noticed was `connect-lifecycle.spec.ts`'s `tags.length > 40`
|
|
11
|
+
// floor. A floor catches the collapse; it does not catch a scan that is merely narrower
|
|
12
|
+
// than it reads, and the next removal will be smaller.
|
|
13
|
+
//
|
|
14
|
+
// So the walk goes ONE LEVEL into the exported objects, which is where `Adw` and `Gtk`
|
|
15
|
+
// put their members. `typeof` is what picks them out and it does not care that they are
|
|
16
|
+
// MODULE namespaces rather than plain objects (`export * as Adw from
|
|
17
|
+
// ./namespace/adw.js`) — a module namespace answers `object` and enumerates under
|
|
18
|
+
// `Object.values` like any other. One level and not a recursion: both barrels are flat
|
|
19
|
+
// by construction, and a deep walk would start reporting whatever a future export
|
|
20
|
+
// happens to hang off itself.
|
|
21
|
+
//
|
|
22
|
+
// It is a module rather than a copy in each spec because the second copy is where a
|
|
23
|
+
// helper gets lifted, and this was already the second copy.
|
|
24
|
+
|
|
25
|
+
/** A constructor the `CustomElementRegistry` could hold — the filter both drivers used. */
|
|
26
|
+
function isElementClass(value: unknown): value is CustomElementConstructor {
|
|
27
|
+
return typeof value === 'function' && value.prototype instanceof HTMLElement;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* The element classes reachable from `barrel`, de-duplicated.
|
|
32
|
+
*
|
|
33
|
+
* De-duplicated because a class can arrive twice: `Adw.CheckBox`-style aliasing is not
|
|
34
|
+
* used today, but a widget that is both a namespace member and a flat web-only export
|
|
35
|
+
* would otherwise be driven twice and counted twice in the floors below the callers.
|
|
36
|
+
*
|
|
37
|
+
* @param barrel the module namespace of `index.ts` (`import * as adwaitaWeb`)
|
|
38
|
+
*/
|
|
39
|
+
export function exportedElementClasses(barrel: Record<string, unknown>): CustomElementConstructor[] {
|
|
40
|
+
const found = new Set<CustomElementConstructor>();
|
|
41
|
+
for (const exported of Object.values(barrel)) {
|
|
42
|
+
if (isElementClass(exported)) {
|
|
43
|
+
found.add(exported);
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
if (typeof exported !== 'object' || exported === null) continue;
|
|
47
|
+
for (const member of Object.values(exported)) if (isElementClass(member)) found.add(member);
|
|
48
|
+
}
|
|
49
|
+
return [...found];
|
|
50
|
+
}
|