@gjsify/adwaita-web 0.36.0 → 0.37.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 +433 -33
- package/dist/adwaita-web.css.map +1 -1
- package/lib/types/adw-shortcut-label.spec.d.ts +2 -0
- package/lib/types/breakpoints.d.ts +10 -12
- package/lib/types/elements/adw-about-dialog.d.ts +19 -36
- package/lib/types/elements/adw-action-row.d.ts +9 -12
- package/lib/types/elements/adw-alert-dialog.d.ts +0 -12
- package/lib/types/elements/adw-avatar.d.ts +1 -0
- package/lib/types/elements/adw-bottom-sheet.d.ts +7 -12
- package/lib/types/elements/adw-button-content.d.ts +3 -4
- package/lib/types/elements/adw-button-row.d.ts +1 -1
- package/lib/types/elements/adw-carousel.d.ts +28 -49
- package/lib/types/elements/adw-checks.d.ts +2 -8
- package/lib/types/elements/adw-combo-row.d.ts +6 -6
- package/lib/types/elements/adw-data-grid.d.ts +5 -10
- package/lib/types/elements/adw-drop-down.d.ts +3 -11
- package/lib/types/elements/adw-entry-row.d.ts +20 -23
- package/lib/types/elements/adw-icon.d.ts +2 -4
- package/lib/types/elements/adw-inline-view-switcher.d.ts +18 -31
- package/lib/types/elements/adw-navigation-split-view.d.ts +7 -12
- package/lib/types/elements/adw-navigation-view.d.ts +2 -10
- package/lib/types/elements/adw-overlay-split-view.d.ts +28 -52
- package/lib/types/elements/adw-password-entry-row.d.ts +4 -5
- package/lib/types/elements/adw-popover.d.ts +2 -7
- package/lib/types/elements/adw-preferences-dialog.d.ts +21 -34
- package/lib/types/elements/adw-preferences-group.d.ts +12 -14
- package/lib/types/elements/adw-progress-bar.d.ts +3 -4
- package/lib/types/elements/adw-shortcut-label.d.ts +20 -0
- package/lib/types/elements/adw-sidebar.d.ts +5 -10
- package/lib/types/elements/adw-spinner.d.ts +3 -5
- package/lib/types/elements/adw-split-button.d.ts +6 -16
- package/lib/types/elements/adw-switch-row.d.ts +3 -3
- package/lib/types/elements/adw-tab-view.d.ts +16 -26
- package/lib/types/elements/adw-toast-overlay.d.ts +8 -13
- package/lib/types/elements/adw-view-stack.d.ts +7 -17
- package/lib/types/elements/adw-view-switcher-bar.d.ts +4 -8
- package/lib/types/elements/adw-view-switcher.d.ts +18 -38
- package/lib/types/elements/adw-wrap-box.d.ts +7 -17
- package/lib/types/elements/view-switcher-dom.d.ts +10 -13
- package/lib/types/index.d.ts +1 -0
- package/lib/types/source-view/hex.d.ts +7 -13
- package/lib/types/style-classes.spec.d.ts +11 -0
- package/lib/types/styles.generated.d.ts +1 -1
- package/package.json +6 -6
- package/scss/_about_dialog.scss +3 -2
- package/scss/_action_row.scss +33 -0
- package/scss/_avatar.scss +12 -0
- package/scss/_button.scss +44 -22
- package/scss/_card.scss +11 -8
- package/scss/_checks.scss +2 -4
- package/scss/_dialog.scss +3 -2
- package/scss/_headerbar.scss +23 -0
- package/scss/_popover.scss +8 -4
- package/scss/_preferences.scss +7 -7
- package/scss/_progress_bar.scss +9 -6
- package/scss/_reset.scss +1 -1
- package/scss/_shortcut_label.scss +80 -0
- package/scss/_sidebar.scss +57 -0
- package/scss/_status_page.scss +20 -0
- package/scss/_style_classes.scss +407 -0
- package/scss/_switch.scss +4 -3
- package/scss/_variables.scss +25 -0
- package/scss/adwaita-skin.scss +12 -0
- package/src/adw-about-dialog.spec.ts +34 -49
- package/src/adw-action-rows.spec.ts +25 -33
- package/src/adw-avatar.spec.ts +51 -20
- package/src/adw-banner.spec.ts +5 -8
- package/src/adw-button-content.spec.ts +9 -12
- package/src/adw-button-row.spec.ts +2 -4
- package/src/adw-carousel.spec.ts +40 -53
- package/src/adw-checks.spec.ts +36 -47
- package/src/adw-drop-down.spec.ts +25 -32
- package/src/adw-header-bar.spec.ts +5 -7
- package/src/adw-icon.spec.ts +23 -32
- package/src/adw-progress-bar.spec.ts +10 -12
- package/src/adw-row-state.spec.ts +9 -10
- package/src/adw-shortcut-label.spec.ts +112 -0
- package/src/adw-sidebar.spec.ts +17 -24
- package/src/adw-switch.spec.ts +11 -17
- package/src/adw-tab-view.spec.ts +7 -10
- package/src/adw-toast-overlay.spec.ts +7 -11
- package/src/adw-view-switcher-bar.spec.ts +10 -14
- package/src/adw-wrap-box.spec.ts +25 -36
- package/src/bottom-sheet.spec.ts +30 -36
- package/src/breakpoints.ts +15 -18
- package/src/chrome.spec.ts +13 -21
- package/src/elements/adw-about-dialog.ts +71 -114
- package/src/elements/adw-action-row.ts +22 -37
- package/src/elements/adw-alert-dialog.ts +25 -50
- package/src/elements/adw-avatar.ts +40 -15
- package/src/elements/adw-banner.ts +14 -18
- package/src/elements/adw-bottom-sheet.ts +31 -52
- package/src/elements/adw-button-content.ts +30 -37
- package/src/elements/adw-button-row.ts +14 -24
- package/src/elements/adw-carousel.ts +87 -148
- package/src/elements/adw-checks.ts +43 -56
- package/src/elements/adw-clamp.ts +8 -9
- package/src/elements/adw-combo-row.ts +17 -25
- package/src/elements/adw-data-grid.ts +26 -38
- package/src/elements/adw-dialog.ts +25 -56
- package/src/elements/adw-drop-down.ts +31 -51
- package/src/elements/adw-entry-row.ts +39 -46
- package/src/elements/adw-expander-row.ts +30 -53
- package/src/elements/adw-icon.ts +30 -53
- package/src/elements/adw-inline-view-switcher.ts +38 -67
- package/src/elements/adw-menu-button.ts +28 -58
- package/src/elements/adw-navigation-split-view.ts +35 -47
- package/src/elements/adw-navigation-view.ts +12 -28
- package/src/elements/adw-overlay-split-view.ts +100 -175
- package/src/elements/adw-password-entry-row.ts +18 -28
- package/src/elements/adw-popover.ts +28 -44
- package/src/elements/adw-preferences-dialog.ts +41 -66
- package/src/elements/adw-preferences-group.ts +33 -46
- package/src/elements/adw-progress-bar.ts +27 -47
- package/src/elements/adw-shortcut-label.ts +138 -0
- package/src/elements/adw-sidebar.ts +31 -39
- package/src/elements/adw-spin-row.ts +7 -14
- package/src/elements/adw-spinner.ts +31 -43
- package/src/elements/adw-split-button.ts +45 -75
- package/src/elements/adw-switch-row.ts +29 -46
- package/src/elements/adw-switch.ts +28 -47
- package/src/elements/adw-tab-view.ts +62 -103
- package/src/elements/adw-toast-overlay.ts +28 -35
- package/src/elements/adw-toggle-group.ts +9 -16
- package/src/elements/adw-view-stack.ts +32 -51
- package/src/elements/adw-view-switcher-bar.ts +26 -50
- package/src/elements/adw-view-switcher.ts +60 -110
- package/src/elements/adw-window-title.ts +4 -4
- package/src/elements/adw-wrap-box.ts +33 -59
- package/src/elements/view-switcher-dom.ts +15 -22
- package/src/entry-rows.spec.ts +18 -24
- package/src/index.ts +13 -15
- package/src/navigation-view.spec.ts +7 -10
- package/src/preferences-search.ts +11 -15
- package/src/preferences.spec.ts +13 -15
- package/src/source-view/adw-source-view.ts +6 -19
- package/src/source-view/hex.ts +7 -13
- package/src/split-button.spec.ts +32 -44
- package/src/split-views.spec.ts +83 -149
- package/src/style-classes.spec.ts +181 -0
- package/src/style-isolation.spec.ts +3 -6
- package/src/styles.generated.ts +1 -1
- package/src/tab-view.spec.ts +26 -35
- package/src/test.browser.mts +4 -0
- package/src/view-stack.spec.ts +23 -34
- package/src/view-switcher.spec.ts +56 -84
package/src/elements/adw-icon.ts
CHANGED
|
@@ -1,43 +1,27 @@
|
|
|
1
|
-
// <adw-icon> — the symbolic icon node
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
// WHY IT EXISTS: `<span class="adw-icon adw-icon--<name>" aria-hidden="true">`
|
|
7
|
-
// was built by hand at TWENTY-THREE sites across SEVENTEEN files, NINE of which
|
|
8
|
-
// re-derived the name (`name.replace(/-symbolic$/, '')`) on the spot — one of
|
|
9
|
-
// them unanchored. Exactly ONE site (`<adw-split-button>`) also checked the
|
|
10
|
-
// result was a single CSS token. The others did not, so `icon-name="a b"`
|
|
11
|
-
// interpolated into `class="adw-icon adw-icon--a b"` and shipped a stray `b`
|
|
12
|
-
// class; that was live on `<adw-menu-button>`, for its own icon and for every
|
|
13
|
-
// JSON menu entry's. The derivation now happens once, in
|
|
14
|
-
// `@gjsify/adwaita-core`'s {@link normalizeIconName} (guard included), and the
|
|
15
|
-
// markup happens once, here.
|
|
1
|
+
// <adw-icon> — the symbolic icon node as an element. Every icon in this package is a
|
|
2
|
+
// CSS-masked box whose glyph comes from a generated `.adw-icon--<name>` class and
|
|
3
|
+
// whose colour is `currentColor`, so it themes with whatever contains it. The name
|
|
4
|
+
// derivation and its guard live once in `@gjsify/adwaita-core`'s
|
|
5
|
+
// {@link normalizeIconName}, the markup once here.
|
|
16
6
|
//
|
|
17
7
|
// Attributes:
|
|
18
|
-
// icon-name — symbolic name, with or without the `-symbolic` suffix. A name
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
// doing exactly that and should NOT set this.
|
|
26
|
-
// Properties:
|
|
27
|
-
// iconName — the declared name (get/set; reflects to the attribute).
|
|
28
|
-
// resolvedIconName — what actually reached the mask class ('' = no icon).
|
|
29
|
-
// size — the px size, or null when the stylesheet decides (get/set).
|
|
8
|
+
// icon-name — symbolic name, with or without the `-symbolic` suffix. A name that is
|
|
9
|
+
// not a single CSS token (a space, a quote, a reverse-DNS application
|
|
10
|
+
// id) resolves to NO icon rather than to injected markup.
|
|
11
|
+
// size — rendered edge length in px. Absent leaves the stylesheet's 16px in
|
|
12
|
+
// charge; a context that sizes its own icons through CSS
|
|
13
|
+
// (`.adw-about-dialog-icon`, the split button's 14px arrows) keeps doing
|
|
14
|
+
// exactly that and should NOT set this.
|
|
30
15
|
//
|
|
31
|
-
// THE ELEMENT *IS* THE ICON BOX — it carries `.adw-icon` itself rather than
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
//
|
|
35
|
-
//
|
|
36
|
-
// matching the same node it always matched.
|
|
16
|
+
// THE ELEMENT *IS* THE ICON BOX — it carries `.adw-icon` itself rather than wrapping a
|
|
17
|
+
// span, because a wrapper would insert a layout node into flex rows, button interiors
|
|
18
|
+
// and absolutely-positioned arrows that are all measured against the icon box, and
|
|
19
|
+
// would stop every class-scoped rule (`.adw-drop-down-arrow`, `.adw-tab-icon`, …)
|
|
20
|
+
// matching the node it matches today.
|
|
37
21
|
//
|
|
38
|
-
// Reference: refs/libadwaita/src/stylesheet/_common.scss
|
|
39
|
-
//
|
|
40
|
-
//
|
|
22
|
+
// Reference: refs/libadwaita/src/stylesheet/_common.scss — `.normal-icons` /
|
|
23
|
+
// `.large-icons` set `-gtk-icon-size`; libadwaita has NO `_icon.scss`, the icon is a
|
|
24
|
+
// GtkImage and its size is a GTK property, not a stylesheet rule.
|
|
41
25
|
// Reference: refs/adwaita-web/adwaita-web/scss/_icon.scss — the vendored web
|
|
42
26
|
// port's `.adw-icon` (an inline-flex SVG holder; this package masks instead).
|
|
43
27
|
// Copyright (c) GNOME contributors (libadwaita). LGPLv2.1+.
|
|
@@ -93,25 +77,20 @@ export class AdwIcon extends HTMLElement {
|
|
|
93
77
|
this._render();
|
|
94
78
|
}
|
|
95
79
|
|
|
96
|
-
// ── internals ──────────────────────────────────────────────────────────
|
|
97
|
-
|
|
98
80
|
private _render(): void {
|
|
99
81
|
this.classList.add('adw-icon');
|
|
100
82
|
|
|
101
|
-
// Swap ONLY the mask class:
|
|
102
|
-
//
|
|
103
|
-
//
|
|
104
|
-
//
|
|
105
|
-
// iterating it would skip the entry after each hit.
|
|
83
|
+
// Swap ONLY the mask class: callers put their own positioning class on the same
|
|
84
|
+
// node, and a `className =` assignment would take those with it. The list is
|
|
85
|
+
// collected BEFORE removing — `classList` is live, so removing while iterating it
|
|
86
|
+
// would skip the entry after each hit.
|
|
106
87
|
const stale = [...this.classList].filter((existing) => existing.startsWith(MASK_CLASS_PREFIX));
|
|
107
88
|
this.classList.remove(...stale);
|
|
108
89
|
const name = this.resolvedIconName;
|
|
109
90
|
if (name !== '') this.classList.add(`${MASK_CLASS_PREFIX}${name}`);
|
|
110
91
|
|
|
111
|
-
// A masked box has no text, so assistive tech has nothing to announce —
|
|
112
|
-
//
|
|
113
|
-
// hand-rolled copy that thought about it said the same thing; the ones
|
|
114
|
-
// that did not were the inconsistency, not the intent.
|
|
92
|
+
// A masked box has no text, so assistive tech has nothing to announce — the
|
|
93
|
+
// accessible name belongs to the control that HOSTS the icon.
|
|
115
94
|
this.setAttribute('aria-hidden', 'true');
|
|
116
95
|
|
|
117
96
|
const size = this.size;
|
|
@@ -124,8 +103,8 @@ export class AdwIcon extends HTMLElement {
|
|
|
124
103
|
const px = `${size}px`;
|
|
125
104
|
this.style.width = px;
|
|
126
105
|
this.style.height = px;
|
|
127
|
-
// The box and the mask scale together
|
|
128
|
-
//
|
|
106
|
+
// The box and the mask scale together: sizing only the box leaves a 16px glyph
|
|
107
|
+
// floating in the middle of it.
|
|
129
108
|
this.style.setProperty('mask-size', px);
|
|
130
109
|
this.style.setProperty('-webkit-mask-size', px);
|
|
131
110
|
}
|
|
@@ -133,10 +112,8 @@ export class AdwIcon extends HTMLElement {
|
|
|
133
112
|
}
|
|
134
113
|
|
|
135
114
|
/**
|
|
136
|
-
* Build one icon node
|
|
137
|
-
*
|
|
138
|
-
* `extraClasses` are the caller's own positioning/context classes, added
|
|
139
|
-
* alongside the managed `.adw-icon` + mask class rather than replacing them.
|
|
115
|
+
* Build one icon node. `extraClasses` are the caller's own positioning/context
|
|
116
|
+
* classes, added alongside the managed `.adw-icon` + mask class, not replacing them.
|
|
140
117
|
*/
|
|
141
118
|
export function createAdwIcon(iconName: string | null, ...extraClasses: string[]): AdwIcon {
|
|
142
119
|
const icon = document.createElement('adw-icon') as AdwIcon;
|
|
@@ -4,28 +4,16 @@
|
|
|
4
4
|
// and exactly one page is visible at a time.
|
|
5
5
|
//
|
|
6
6
|
// Everything that decides WHAT to show is HEADLESS and lives in
|
|
7
|
-
// `@gjsify/adwaita-core` (ADR 0004) — including the TWO INDEX SPACES this widget
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
// never has to reconcile them itself.
|
|
7
|
+
// `@gjsify/adwaita-core` (ADR 0004) — including the TWO INDEX SPACES this widget is
|
|
8
|
+
// built on: a hidden page produces NO toggle, so the compacted toggle index and the
|
|
9
|
+
// page index diverge, and libadwaita reconciles them through the `child-index` it
|
|
10
|
+
// stashes on every toggle. `buildInlineToggles` carries both indices, so this file
|
|
11
|
+
// never reconciles them itself.
|
|
13
12
|
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
// - an unrecognised display-mode value REPLACED the current mode with 'both';
|
|
19
|
-
// `g_return_if_fail (mode <= …_BOTH)` rejects it and keeps the current one
|
|
20
|
-
// (:819).
|
|
21
|
-
// - the icons-mode tooltip used the RAW title, so `title="_Files"` showed the
|
|
22
|
-
// underscore; C strips the mnemonic when use-underline is set (:180-188).
|
|
23
|
-
// - a page with no icon rendered no icon; C substitutes `image-missing`
|
|
24
|
-
// (:137-142) in every mode that builds an image at all.
|
|
25
|
-
// - `active` was CLAMPED into range instead of refused, `notify::display-mode`
|
|
26
|
-
// was documented but never dispatched, the page query was unscoped (so a
|
|
27
|
-
// nested switcher lost its pages to the outer one), and the 500 ms
|
|
28
|
-
// drag-hover auto-switch did not exist (:80, :194-236).
|
|
13
|
+
// Two of the core's derivations are easy to get wrong by hand: the icons-mode tooltip
|
|
14
|
+
// STRIPS the mnemonic when use-underline is set (`title="_Files"` must not show the
|
|
15
|
+
// underscore), and a page with no icon gets `image-missing` substituted in every mode
|
|
16
|
+
// that builds an image at all.
|
|
29
17
|
//
|
|
30
18
|
// Attributes:
|
|
31
19
|
// active — zero-based index of the visible PAGE (not of the toggle). Refused,
|
|
@@ -77,14 +65,10 @@ export class AdwViewStackPage extends HTMLElement {
|
|
|
77
65
|
|
|
78
66
|
export class AdwInlineViewSwitcher extends HTMLElement {
|
|
79
67
|
/**
|
|
80
|
-
* The dwell timer, injectable
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
* `VIEW_SWITCHER_DRAG_VECTORS` was driven by the core suite alone, and the
|
|
85
|
-
* conformance header claimed all three suites drove it. `scheduler` is a
|
|
86
|
-
* property so a test can replace it before the element connects; the DOM one
|
|
87
|
-
* is the default, exactly as before.
|
|
68
|
+
* The dwell timer, injectable: a property rather than an inline
|
|
69
|
+
* `new ViewSwitcherState({ scheduler: domViewSwitcherScheduler })`, so a test can
|
|
70
|
+
* replace the clock before the element connects and drive
|
|
71
|
+
* `VIEW_SWITCHER_DRAG_VECTORS` here too. The DOM scheduler is the default.
|
|
88
72
|
*/
|
|
89
73
|
scheduler: ViewSwitcherScheduler = domViewSwitcherScheduler;
|
|
90
74
|
private _stateInstance: ViewSwitcherState | null = null;
|
|
@@ -127,14 +111,12 @@ export class AdwInlineViewSwitcher extends HTMLElement {
|
|
|
127
111
|
|
|
128
112
|
/**
|
|
129
113
|
* Index of the ACTIVE TOGGLE, `-1` when the visible page has none because it
|
|
130
|
-
* is hidden — the port-safe `GTK_INVALID_LIST_POSITION
|
|
131
|
-
* (adw-inline-view-switcher.c:441-446).
|
|
114
|
+
* is hidden — the port-safe `GTK_INVALID_LIST_POSITION`.
|
|
132
115
|
*/
|
|
133
116
|
get activeToggle(): number {
|
|
134
117
|
return toggleIndexForPage(this._state.pages, this._state.selected);
|
|
135
118
|
}
|
|
136
119
|
|
|
137
|
-
/** What the toggles display: 'labels', 'icons' or 'both'. */
|
|
138
120
|
get displayMode(): AdwInlineViewSwitcherDisplayMode {
|
|
139
121
|
return this._displayMode;
|
|
140
122
|
}
|
|
@@ -152,7 +134,6 @@ export class AdwInlineViewSwitcher extends HTMLElement {
|
|
|
152
134
|
this._state.selectName(name);
|
|
153
135
|
}
|
|
154
136
|
|
|
155
|
-
/** The resolved page records. */
|
|
156
137
|
get pages(): readonly AdwViewSwitcherPage[] {
|
|
157
138
|
return this._state.pages;
|
|
158
139
|
}
|
|
@@ -160,8 +141,7 @@ export class AdwInlineViewSwitcher extends HTMLElement {
|
|
|
160
141
|
/**
|
|
161
142
|
* Show or hide a page. Returns whether the SELECTION moved. Rebuilds the
|
|
162
143
|
* toggle row, because a hidden page has no toggle at all — C reacts to a
|
|
163
|
-
* page's `notify::visible` with `recreate_toggles
|
|
164
|
-
* (adw-inline-view-switcher.c:425, :471-477).
|
|
144
|
+
* page's `notify::visible` with `recreate_toggles`.
|
|
165
145
|
*/
|
|
166
146
|
setPageVisible(name: string, visible: boolean): boolean {
|
|
167
147
|
const moved = this._state.setPageVisible(name, visible);
|
|
@@ -222,11 +202,9 @@ export class AdwInlineViewSwitcher extends HTMLElement {
|
|
|
222
202
|
}
|
|
223
203
|
|
|
224
204
|
/**
|
|
225
|
-
* Re-read every declared page and push it into the state.
|
|
226
|
-
*
|
|
227
|
-
*
|
|
228
|
-
* `badge-number` or `icon-name` change reach the toggle — C rebinds on every
|
|
229
|
-
* page `notify` (adw-view-switcher.c:184-193) and this element did not.
|
|
205
|
+
* Re-read every declared page and push it into the state. Driven by
|
|
206
|
+
* {@link _watchPages}, which is what makes a runtime `badge-number` or `icon-name`
|
|
207
|
+
* change reach the toggle, as C's per-page `notify` bindings do.
|
|
230
208
|
*/
|
|
231
209
|
refreshPages(): void {
|
|
232
210
|
if (!this._initialized) return;
|
|
@@ -236,13 +214,11 @@ export class AdwInlineViewSwitcher extends HTMLElement {
|
|
|
236
214
|
|
|
237
215
|
/**
|
|
238
216
|
* Watch the declared pages for attribute changes — the DOM's `notify::` on
|
|
239
|
-
* `AdwViewStackPage
|
|
240
|
-
*
|
|
241
|
-
*
|
|
242
|
-
*
|
|
243
|
-
*
|
|
244
|
-
* a MICROTASK where GTK's notify is synchronous — invisible to a user, and
|
|
245
|
-
* nothing here reads the toggle back inside the setter.
|
|
217
|
+
* `AdwViewStackPage`. A `MutationObserver` rather than an
|
|
218
|
+
* `attributeChangedCallback`, because this element consumes the page elements at
|
|
219
|
+
* connect and `replaceChildren`s them out of the tree, so a detached page cannot
|
|
220
|
+
* find its owner. It delivers on a MICROTASK where GTK's notify is synchronous —
|
|
221
|
+
* invisible to a user, and nothing here reads the toggle back inside the setter.
|
|
246
222
|
*/
|
|
247
223
|
private _watchPages(): void {
|
|
248
224
|
this._pageObserver?.disconnect();
|
|
@@ -264,14 +240,13 @@ export class AdwInlineViewSwitcher extends HTMLElement {
|
|
|
264
240
|
if (next === this._displayMode) return;
|
|
265
241
|
this._displayMode = next;
|
|
266
242
|
// C rebuilds every toggle's child on a mode change rather than
|
|
267
|
-
// toggling visibility on the existing ones
|
|
243
|
+
// toggling visibility on the existing ones.
|
|
268
244
|
this._rebuildToggles();
|
|
269
245
|
this.dispatchEvent(
|
|
270
246
|
new CustomEvent('notify::display-mode', { bubbles: true, detail: { displayMode: next } }),
|
|
271
247
|
);
|
|
272
248
|
return;
|
|
273
249
|
}
|
|
274
|
-
// flat / round are styling-only.
|
|
275
250
|
this._applyStyleClasses();
|
|
276
251
|
}
|
|
277
252
|
|
|
@@ -282,11 +257,10 @@ export class AdwInlineViewSwitcher extends HTMLElement {
|
|
|
282
257
|
}
|
|
283
258
|
|
|
284
259
|
/**
|
|
285
|
-
* Rebuild the toggle row from the derived models
|
|
286
|
-
*
|
|
287
|
-
*
|
|
288
|
-
*
|
|
289
|
-
* the dwell the drag has already armed.
|
|
260
|
+
* Rebuild the toggle row from the derived models: one node per VISIBLE page, in
|
|
261
|
+
* compacted order — the structural half of `recreate_toggles`. Selection alone never
|
|
262
|
+
* lands here (`selection_changed_cb` only sets the active toggle), because
|
|
263
|
+
* rebuilding mid-drag would drop the dwell the drag has already armed.
|
|
290
264
|
*/
|
|
291
265
|
private _rebuildToggles(): void {
|
|
292
266
|
if (!this._initialized) return;
|
|
@@ -327,13 +301,12 @@ export class AdwInlineViewSwitcher extends HTMLElement {
|
|
|
327
301
|
toggle.addEventListener('click', () => {
|
|
328
302
|
this._state.setSelected(model.pageIndex);
|
|
329
303
|
});
|
|
330
|
-
// The `relatedTarget` test is load-bearing: a toggle holds an icon,
|
|
331
|
-
//
|
|
332
|
-
//
|
|
333
|
-
//
|
|
334
|
-
//
|
|
335
|
-
//
|
|
336
|
-
// (adw-view-switcher-button.c:79-96), which has no internal edges.
|
|
304
|
+
// The `relatedTarget` test is load-bearing: a toggle holds an icon, a label
|
|
305
|
+
// and an indicator, and the DOM fires `dragleave` + `dragenter` when the
|
|
306
|
+
// pointer crosses BETWEEN them, which clears the dwell timer and re-arms a
|
|
307
|
+
// fresh 500 ms — so a drag that merely slid across the toggle would never
|
|
308
|
+
// switch the page. C has one `GtkDropControllerMotion` per BUTTON widget,
|
|
309
|
+
// which has no internal edges.
|
|
337
310
|
toggle.addEventListener('dragenter', (event) => {
|
|
338
311
|
if (toggle.contains((event as DragEvent).relatedTarget as Node | null)) return;
|
|
339
312
|
this._state.dragEnter(model.pageIndex);
|
|
@@ -389,18 +362,16 @@ export class AdwInlineViewSwitcher extends HTMLElement {
|
|
|
389
362
|
}
|
|
390
363
|
|
|
391
364
|
private _applyStyleClasses(): void {
|
|
392
|
-
// `css_classes_changed_cb`
|
|
393
|
-
//
|
|
394
|
-
//
|
|
395
|
-
// `osd` inline switcher — the one in a floating overlay, where the class
|
|
396
|
-
// exists to make it legible — styled nothing at all.
|
|
365
|
+
// `css_classes_changed_cb` forwards THREE classes onto the toggle group, not
|
|
366
|
+
// two: `flat`, `round` AND `osd` — the last is what makes an inline switcher in a
|
|
367
|
+
// floating overlay legible, so dropping it styles nothing.
|
|
397
368
|
for (const name of ['flat', 'round', 'osd'] as const) {
|
|
398
369
|
const present = this.hasAttribute(name);
|
|
399
370
|
this.classList.toggle(name, present);
|
|
400
371
|
this._groupEl?.classList.toggle(name, present);
|
|
401
372
|
}
|
|
402
373
|
// The group carries the display-mode class, mirroring libadwaita's
|
|
403
|
-
// inline-view-switcher > toggle-group.{labels,icons,both}
|
|
374
|
+
// `inline-view-switcher > toggle-group.{labels,icons,both}`.
|
|
404
375
|
for (const mode of INLINE_VIEW_SWITCHER_DISPLAY_MODES) {
|
|
405
376
|
this._groupEl.classList.toggle(mode, mode === this._displayMode);
|
|
406
377
|
}
|
|
@@ -1,43 +1,19 @@
|
|
|
1
|
-
// <adw-menu-button> —
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
// real Adwaita-styled popover positioned under the button; the popover is
|
|
6
|
-
// dismissed on an outside click or Escape and supports arrow-key navigation.
|
|
1
|
+
// <adw-menu-button> — the web counterpart of Adw.MenuButton / Gtk.MenuButton: by
|
|
2
|
+
// default the flat `open-menu-symbolic` app-menu button used at the end of a header
|
|
3
|
+
// bar. Clicking it toggles an Adwaita-styled popover under the button, dismissed on
|
|
4
|
+
// an outside click or Escape, with arrow-key navigation.
|
|
7
5
|
//
|
|
8
6
|
// The popover is `<adw-popover>` and the dismissal/keyboard machine is
|
|
9
|
-
// `@gjsify/adwaita-core`'s (ADR 0004)
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
// keeps only what is a menu button: the icon, the item model, the title.
|
|
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 `<adw-icon>`, whose
|
|
9
|
+
// `normalizeIconName` guard is what keeps a multi-token `icon-name="a b"` from
|
|
10
|
+
// shipping a stray CSS class — for the button icon and for every menu entry's.
|
|
14
11
|
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
// did every JSON menu entry's `icon`. Both are `<adw-icon>` now, and the guard
|
|
20
|
-
// is `normalizeIconName`'s.
|
|
12
|
+
// The `menu` attribute is a JSON array of `{ "id"?, "label", "icon"? }`. Choosing an
|
|
13
|
+
// item closes the popover and fires `menu-item-activated` (CustomEvent, bubbles,
|
|
14
|
+
// detail `{ id, label, index }`) with `id` falling back to `label`, matching the NS
|
|
15
|
+
// twin.
|
|
21
16
|
//
|
|
22
|
-
// Attributes:
|
|
23
|
-
// icon-name — symbolic icon (no `-symbolic` suffix), default `open-menu`.
|
|
24
|
-
// menu-title — optional heading shown atop the popover.
|
|
25
|
-
// menu — JSON array of items, each `{ "id"?, "label", "icon"? }`.
|
|
26
|
-
// disabled — boolean; a disabled button does not open the menu.
|
|
27
|
-
// flat — boolean style class (default: flat, like the header app menu).
|
|
28
|
-
// circular — boolean style class (round 34×34 button).
|
|
29
|
-
// direction — none | up | down | left | right (default down); where the
|
|
30
|
-
// popover opens. `none` behaves as `down`, which is
|
|
31
|
-
// Gtk.MenuButton's own rule, not ours — see
|
|
32
|
-
// {@link menuButtonPopupDirection}.
|
|
33
|
-
// Properties (mirroring Adw.MenuButton / the NS twin):
|
|
34
|
-
// menuItems — the menu entries ({ id?, label, icon? }[]) (get/set).
|
|
35
|
-
// menuTitle — the popover heading (get/set).
|
|
36
|
-
// active — whether the popover is open (get).
|
|
37
|
-
// Events:
|
|
38
|
-
// `menu-item-activated` (CustomEvent, bubbles, detail = { id, label, index })
|
|
39
|
-
// when an item is chosen — `id` falls back to `label` when omitted (matches
|
|
40
|
-
// the NS twin). The popover closes on activation.
|
|
41
17
|
// Reference: refs/libadwaita/src/adw-menu-button.c (AdwMenuButton)
|
|
42
18
|
// Reference: refs/libadwaita/src/stylesheet/widgets/_menus.scss (popover.menu / modelbutton)
|
|
43
19
|
// Reference: packages/nativescript-bridge/adwaita/src/widgets/adw-menu-button.ts (NS twin)
|
|
@@ -47,13 +23,12 @@
|
|
|
47
23
|
import { isSplitButtonDirection, menuButtonPopupDirection } from '@gjsify/adwaita-core';
|
|
48
24
|
import type { SplitButtonDirection } from '@gjsify/adwaita-core';
|
|
49
25
|
|
|
50
|
-
// SIDE-EFFECT import, deliberately separate from the type import below: it
|
|
51
|
-
//
|
|
52
|
-
//
|
|
53
|
-
//
|
|
54
|
-
//
|
|
55
|
-
//
|
|
56
|
-
// the registration with it.
|
|
26
|
+
// SIDE-EFFECT import, deliberately separate from the type import below: it guarantees
|
|
27
|
+
// `adw-popover` is defined before this module's `customElements.define` can upgrade a
|
|
28
|
+
// server-rendered `<adw-menu-button>` and build one. A combined
|
|
29
|
+
// `import { AdwPopover }` would NOT do it — the binding is only used in type position,
|
|
30
|
+
// and this package compiles without `verbatimModuleSyntax`, so TypeScript would elide
|
|
31
|
+
// the statement and take the registration with it.
|
|
57
32
|
import './adw-popover.js';
|
|
58
33
|
import type { AdwPopover } from './adw-popover.js';
|
|
59
34
|
|
|
@@ -67,14 +42,12 @@ export interface AdwMenuItem {
|
|
|
67
42
|
}
|
|
68
43
|
|
|
69
44
|
/**
|
|
70
|
-
* Where the popover sits, per `GtkArrowType`. `menuButtonPopupDirection` folds
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
* only the direction→CSS-axis mapping, which is a renderer fact.
|
|
45
|
+
* Where the popover sits, per `GtkArrowType`. `menuButtonPopupDirection` folds `none`
|
|
46
|
+
* onto `down` — GtkMenuButton's rule, not ours; this table is only the
|
|
47
|
+
* direction→CSS-axis mapping, which is a renderer fact.
|
|
74
48
|
*
|
|
75
|
-
* `left`/`right` become `start`/`end` because the surface is placed with
|
|
76
|
-
*
|
|
77
|
-
* `:dir(rtl)` popovers do.
|
|
49
|
+
* `left`/`right` become `start`/`end` because the surface is placed with logical
|
|
50
|
+
* properties, so it follows the writing direction as GTK's `:dir(rtl)` popovers do.
|
|
78
51
|
*/
|
|
79
52
|
const POPOVER_POSITIONS = {
|
|
80
53
|
down: 'bottom',
|
|
@@ -163,9 +136,8 @@ export class AdwMenuButton extends HTMLElement {
|
|
|
163
136
|
|
|
164
137
|
// Seed items from the `menu` attribute if the property was not set.
|
|
165
138
|
if (this._items.length === 0) this._items = this._parseMenuAttr();
|
|
166
|
-
//
|
|
167
|
-
//
|
|
168
|
-
// renders) never runs before `_buttonEl` is created.
|
|
139
|
+
// The flat default is set only now the DOM exists, so the observed-attribute
|
|
140
|
+
// callback (which renders) never runs before `_buttonEl` is created.
|
|
169
141
|
if (!this.hasAttribute('flat') && !this.hasAttribute('circular')) this.setAttribute('flat', '');
|
|
170
142
|
this._render();
|
|
171
143
|
}
|
|
@@ -197,7 +169,6 @@ export class AdwMenuButton extends HTMLElement {
|
|
|
197
169
|
private _onPopoverToggled(open: boolean): void {
|
|
198
170
|
this.classList.toggle('active', open);
|
|
199
171
|
this._buttonEl.setAttribute('aria-expanded', String(open));
|
|
200
|
-
// Move focus into the menu for keyboard navigation.
|
|
201
172
|
if (open) this._itemButtons[0]?.focus();
|
|
202
173
|
}
|
|
203
174
|
|
|
@@ -238,9 +209,9 @@ export class AdwMenuButton extends HTMLElement {
|
|
|
238
209
|
item.setAttribute('role', 'menuitem');
|
|
239
210
|
item.tabIndex = -1;
|
|
240
211
|
|
|
241
|
-
// An entry with no USABLE icon gets no icon node at all
|
|
242
|
-
//
|
|
243
|
-
//
|
|
212
|
+
// An entry with no USABLE icon gets no icon node at all rather than an empty
|
|
213
|
+
// 16px box; `<adw-icon>` itself answers whether the name resolved, so the
|
|
214
|
+
// `-symbolic`-only and bad-token cases agree.
|
|
244
215
|
const entryIcon = createAdwIcon(entry.icon ?? null, 'adw-menu-button-item-icon');
|
|
245
216
|
if (entryIcon.resolvedIconName !== '') item.appendChild(entryIcon);
|
|
246
217
|
const labelEl = document.createElement('span');
|
|
@@ -262,7 +233,6 @@ export class AdwMenuButton extends HTMLElement {
|
|
|
262
233
|
this._itemButtons.push(item);
|
|
263
234
|
}
|
|
264
235
|
|
|
265
|
-
// A menu-button with no entries can never open.
|
|
266
236
|
if (this._items.length === 0) this._popoverEl.popdown();
|
|
267
237
|
}
|
|
268
238
|
}
|