@gjsify/adwaita-web 0.44.0 → 0.46.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-header-bar.d.ts +16 -8
- 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 +9 -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/adw-header-bar.spec.ts +9 -4
- package/src/adw-tab-view.spec.ts +71 -0
- 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-header-bar.ts +34 -13
- package/src/elements/adw-navigation-view.ts +24 -4
- 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 +32 -10
- 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
|
@@ -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
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// DOM-level tests for <
|
|
1
|
+
// DOM-level tests for <gtk-button>'s style classes.
|
|
2
2
|
//
|
|
3
3
|
// The element and its NativeScript twin each kept their own attribute→class table,
|
|
4
4
|
// and the two disagreed: `circular` was here and not there. The table now lives in
|
|
@@ -13,7 +13,7 @@ const WEB_ATTRIBUTES = new Set(['flat', 'suggested', 'destructive', 'circular',
|
|
|
13
13
|
|
|
14
14
|
/** Mount a button carrying the given boolean attributes; return the inner classes. */
|
|
15
15
|
function mountWithStyles(names: readonly string[]): string[] {
|
|
16
|
-
const el = document.createElement('
|
|
16
|
+
const el = document.createElement('gtk-button');
|
|
17
17
|
el.setAttribute('label', 'Click me');
|
|
18
18
|
for (const name of names) el.setAttribute(name, '');
|
|
19
19
|
document.body.appendChild(el);
|
|
@@ -22,10 +22,10 @@ function mountWithStyles(names: readonly string[]): string[] {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
function unmountAll(): void {
|
|
25
|
-
for (const el of Array.from(document.querySelectorAll('
|
|
25
|
+
for (const el of Array.from(document.querySelectorAll('gtk-button'))) el.remove();
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
export const
|
|
28
|
+
export const GtkButtonTest = async () => {
|
|
29
29
|
// Rows naming a class the LONG way (`suggested-action`) are the NativeScript
|
|
30
30
|
// variant spelling — this element has no such attribute. They are partitioned
|
|
31
31
|
// out here rather than skipped inside the loop, and the partition is asserted,
|
|
@@ -33,7 +33,7 @@ export const AdwButtonTest = async () => {
|
|
|
33
33
|
const reachable = BUTTON_STYLE_CLASS_VECTORS.filter((v) => v.names.every((n) => WEB_ATTRIBUTES.has(n)));
|
|
34
34
|
const unreachable = BUTTON_STYLE_CLASS_VECTORS.filter((v) => !v.names.every((n) => WEB_ATTRIBUTES.has(n)));
|
|
35
35
|
|
|
36
|
-
await describe('<
|
|
36
|
+
await describe('<gtk-button> style classes', async () => {
|
|
37
37
|
for (const { names, classes, rule } of reachable) {
|
|
38
38
|
await it(`[${names.join(', ')}] → [${classes.join(', ')}] — ${rule}`, () => {
|
|
39
39
|
expect(mountWithStyles(names)).toStrictEqual([...classes]);
|
|
@@ -47,7 +47,7 @@ export const AdwButtonTest = async () => {
|
|
|
47
47
|
});
|
|
48
48
|
|
|
49
49
|
await it('a style attribute added later is picked up', () => {
|
|
50
|
-
const el = document.createElement('
|
|
50
|
+
const el = document.createElement('gtk-button');
|
|
51
51
|
el.setAttribute('label', 'Send');
|
|
52
52
|
document.body.appendChild(el);
|
|
53
53
|
el.setAttribute('suggested', '');
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// DOM-level behaviour tests for <
|
|
1
|
+
// DOM-level behaviour tests for <gtk-drop-down>, in a real browser via the
|
|
2
2
|
// @gjsify/adwaita-web browser test axis.
|
|
3
3
|
//
|
|
4
4
|
// The SELECTION half is driven by the SAME table the NativeScript renderer asserts
|
|
@@ -14,21 +14,21 @@ import { describe, it, expect } from '@gjsify/unit';
|
|
|
14
14
|
import { COMBO_SELECTION_VECTORS } from '@gjsify/adwaita-core/conformance';
|
|
15
15
|
import type { ComboSelectionStep, ComboSelectionVector } from '@gjsify/adwaita-core/conformance';
|
|
16
16
|
|
|
17
|
-
import type {
|
|
17
|
+
import type { GtkDropDown } from './elements/gtk-drop-down.js';
|
|
18
18
|
|
|
19
|
-
function makeDropDown():
|
|
20
|
-
const el = document.createElement('
|
|
19
|
+
function makeDropDown(): GtkDropDown {
|
|
20
|
+
const el = document.createElement('gtk-drop-down') as GtkDropDown;
|
|
21
21
|
document.body.appendChild(el);
|
|
22
22
|
return el;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
/** The text the closed button shows — the rendered `selectedLabel`. */
|
|
26
|
-
function labelText(dd:
|
|
26
|
+
function labelText(dd: GtkDropDown): string {
|
|
27
27
|
return dd.querySelector('.adw-drop-down-label')?.textContent ?? '';
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
/** Open the chooser the way a user does. A no-op over an empty model, as the element guards. */
|
|
31
|
-
function openChooser(dd:
|
|
31
|
+
function openChooser(dd: GtkDropDown): void {
|
|
32
32
|
(dd.querySelector('.adw-drop-down-button') as HTMLButtonElement).click();
|
|
33
33
|
}
|
|
34
34
|
|
|
@@ -36,7 +36,7 @@ function openChooser(dd: AdwDropDown): void {
|
|
|
36
36
|
* Replay one vector step against the real element — property sets for the
|
|
37
37
|
* programmatic ops, a popover gesture for the interactive one.
|
|
38
38
|
*/
|
|
39
|
-
function applyStep(dd:
|
|
39
|
+
function applyStep(dd: GtkDropDown, step: ComboSelectionStep): void {
|
|
40
40
|
switch (step.op) {
|
|
41
41
|
case 'setOptions':
|
|
42
42
|
dd.options = step.options;
|
|
@@ -78,15 +78,15 @@ function vectorNamed(name: string): ComboSelectionVector {
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
/** Mount a drop-down and record every `change` detail it dispatches. */
|
|
81
|
-
function mountRecording(): { dd:
|
|
81
|
+
function mountRecording(): { dd: GtkDropDown; changes: unknown[] } {
|
|
82
82
|
const dd = makeDropDown();
|
|
83
83
|
const changes: unknown[] = [];
|
|
84
84
|
dd.addEventListener('change', (event) => changes.push((event as CustomEvent).detail));
|
|
85
85
|
return { dd, changes };
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
export const
|
|
89
|
-
await describe('
|
|
88
|
+
export const GtkDropDownTest = async () => {
|
|
89
|
+
await describe('gtk-drop-down selection (libadwaita conformance vectors)', async () => {
|
|
90
90
|
for (const vector of COMBO_SELECTION_VECTORS) {
|
|
91
91
|
if (vector.name === DROP_DOWN_REJECTS) continue;
|
|
92
92
|
|
|
@@ -129,7 +129,7 @@ export const AdwDropDownTest = async () => {
|
|
|
129
129
|
});
|
|
130
130
|
});
|
|
131
131
|
|
|
132
|
-
await describe('
|
|
132
|
+
await describe('gtk-drop-down options', async () => {
|
|
133
133
|
await it('accepts a string[] (value === label)', async () => {
|
|
134
134
|
const dd = makeDropDown();
|
|
135
135
|
dd.options = ['One', 'Two', 'Three'];
|
|
@@ -152,7 +152,7 @@ export const AdwDropDownTest = async () => {
|
|
|
152
152
|
});
|
|
153
153
|
|
|
154
154
|
await it('parses options from the JSON attribute', async () => {
|
|
155
|
-
const dd = document.createElement('
|
|
155
|
+
const dd = document.createElement('gtk-drop-down') as GtkDropDown;
|
|
156
156
|
dd.setAttribute('options', '[{"value":"x","label":"Ex"},{"value":"y","label":"Why"}]');
|
|
157
157
|
dd.setAttribute('selected', '1');
|
|
158
158
|
document.body.appendChild(dd);
|
|
@@ -170,7 +170,7 @@ export const AdwDropDownTest = async () => {
|
|
|
170
170
|
});
|
|
171
171
|
});
|
|
172
172
|
|
|
173
|
-
await describe('
|
|
173
|
+
await describe('gtk-drop-down selected ↔ value sync', async () => {
|
|
174
174
|
await it('selectedValue setter finds the index by value', async () => {
|
|
175
175
|
const dd = makeDropDown();
|
|
176
176
|
dd.options = [
|
|
@@ -205,7 +205,7 @@ export const AdwDropDownTest = async () => {
|
|
|
205
205
|
await it('setting options + selected before connect does not crash + applies on connect', async () => {
|
|
206
206
|
// The property setters can run BEFORE connectedCallback builds the DOM, where
|
|
207
207
|
// _updateLabel would touch an undefined label element.
|
|
208
|
-
const dd = document.createElement('
|
|
208
|
+
const dd = document.createElement('gtk-drop-down') as GtkDropDown;
|
|
209
209
|
dd.options = ['a', 'b', 'c'];
|
|
210
210
|
dd.selected = 2;
|
|
211
211
|
expect(dd.isConnected).toBe(false);
|
|
@@ -217,7 +217,7 @@ export const AdwDropDownTest = async () => {
|
|
|
217
217
|
});
|
|
218
218
|
});
|
|
219
219
|
|
|
220
|
-
await describe('
|
|
220
|
+
await describe('gtk-drop-down notify-on-change', async () => {
|
|
221
221
|
await it('a programmatic set fires notify::selected but NOT change (DOM <select> semantics)', async () => {
|
|
222
222
|
const dd = makeDropDown();
|
|
223
223
|
dd.options = ['a', 'b', 'c'];
|
|
@@ -259,7 +259,7 @@ export const AdwDropDownTest = async () => {
|
|
|
259
259
|
});
|
|
260
260
|
});
|
|
261
261
|
|
|
262
|
-
await describe('
|
|
262
|
+
await describe('gtk-drop-down popover behaviour', async () => {
|
|
263
263
|
await it('opens on button click and closes on outside pointerdown', async () => {
|
|
264
264
|
const dd = makeDropDown();
|
|
265
265
|
dd.options = ['a', 'b'];
|
|
@@ -286,7 +286,7 @@ export const AdwDropDownTest = async () => {
|
|
|
286
286
|
});
|
|
287
287
|
});
|
|
288
288
|
|
|
289
|
-
await describe('
|
|
289
|
+
await describe('gtk-drop-down search', async () => {
|
|
290
290
|
await it('renders a search entry and filters the list', async () => {
|
|
291
291
|
const dd = makeDropDown();
|
|
292
292
|
dd.options = ['Apple', 'Banana', 'Cherry'];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// DOM-level tests for <
|
|
1
|
+
// DOM-level tests for <gtk-entry>'s length arithmetic.
|
|
2
2
|
//
|
|
3
3
|
// The element had NO max length and no text length, while its NativeScript twin has
|
|
4
4
|
// composed `@gjsify/adwaita-core`'s since it shipped — so the same consumer got a
|
|
@@ -10,22 +10,22 @@
|
|
|
10
10
|
import { describe, expect, it } from '@gjsify/unit';
|
|
11
11
|
import { ENTRY_MAX_LENGTH_VECTORS, ENTRY_TEXT_LENGTH_VECTORS } from '@gjsify/adwaita-core/conformance';
|
|
12
12
|
|
|
13
|
-
import type {
|
|
13
|
+
import type { GtkEntry } from './elements/gtk-entry.js';
|
|
14
14
|
|
|
15
15
|
/** Mount an entry with the given attributes. */
|
|
16
|
-
function mountEntry(attrs: Record<string, string> = {}):
|
|
17
|
-
const entry = document.createElement('
|
|
16
|
+
function mountEntry(attrs: Record<string, string> = {}): GtkEntry {
|
|
17
|
+
const entry = document.createElement('gtk-entry') as GtkEntry;
|
|
18
18
|
for (const [name, value] of Object.entries(attrs)) entry.setAttribute(name, value);
|
|
19
19
|
document.body.appendChild(entry);
|
|
20
20
|
return entry;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
function unmountAll(): void {
|
|
24
|
-
for (const entry of Array.from(document.querySelectorAll('
|
|
24
|
+
for (const entry of Array.from(document.querySelectorAll('gtk-entry'))) entry.remove();
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
export const
|
|
28
|
-
await describe('<
|
|
27
|
+
export const GtkEntryTest = async () => {
|
|
28
|
+
await describe('<gtk-entry> text length', async () => {
|
|
29
29
|
for (const { text, length, rule } of ENTRY_TEXT_LENGTH_VECTORS) {
|
|
30
30
|
await it(`${JSON.stringify(text)} → ${length} — ${rule}`, () => {
|
|
31
31
|
const entry = mountEntry();
|
|
@@ -36,7 +36,7 @@ export const AdwEntryTest = async () => {
|
|
|
36
36
|
}
|
|
37
37
|
});
|
|
38
38
|
|
|
39
|
-
await describe('<
|
|
39
|
+
await describe('<gtk-entry> max length', async () => {
|
|
40
40
|
for (const { text, maxLength, clamped, length, rule } of ENTRY_MAX_LENGTH_VECTORS) {
|
|
41
41
|
await it(`${JSON.stringify(text)} @ ${maxLength} — ${rule}`, () => {
|
|
42
42
|
const entry = mountEntry({ maxlength: String(maxLength) });
|