@gjsify/adwaita-web 0.31.0 → 0.32.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adwaita-web.css +786 -272
- package/dist/adwaita-web.css.map +1 -1
- package/lib/types/adw-about-dialog.spec.d.ts +1 -0
- package/lib/types/adw-action-rows.spec.d.ts +1 -0
- package/lib/types/adw-avatar.spec.d.ts +1 -0
- package/lib/types/adw-banner.spec.d.ts +1 -0
- package/lib/types/adw-button-content.spec.d.ts +1 -0
- package/lib/types/adw-carousel.spec.d.ts +1 -0
- package/lib/types/adw-checks.spec.d.ts +1 -0
- package/lib/types/adw-header-bar.spec.d.ts +1 -0
- package/lib/types/adw-icon.spec.d.ts +1 -0
- package/lib/types/adw-progress-bar.spec.d.ts +1 -0
- package/lib/types/adw-sidebar.spec.d.ts +1 -0
- package/lib/types/adw-switch.spec.d.ts +1 -0
- package/lib/types/adw-wrap-box.spec.d.ts +1 -0
- package/lib/types/bottom-sheet.spec.d.ts +1 -0
- package/lib/types/breakpoints.d.ts +22 -0
- package/lib/types/breakpoints.spec.d.ts +1 -0
- package/lib/types/chrome.spec.d.ts +1 -0
- package/lib/types/elements/adw-about-dialog.d.ts +47 -8
- package/lib/types/elements/adw-action-row.d.ts +35 -0
- package/lib/types/elements/adw-avatar.d.ts +11 -0
- package/lib/types/elements/adw-bottom-sheet.d.ts +33 -4
- package/lib/types/elements/adw-button-content.d.ts +12 -0
- package/lib/types/elements/adw-button-row.d.ts +10 -1
- package/lib/types/elements/adw-carousel.d.ts +111 -8
- package/lib/types/elements/adw-checks.d.ts +57 -0
- package/lib/types/elements/adw-clamp.d.ts +4 -1
- package/lib/types/elements/adw-data-grid.d.ts +11 -31
- package/lib/types/elements/adw-drop-down.d.ts +17 -20
- package/lib/types/elements/adw-entry-row.d.ts +93 -3
- package/lib/types/elements/adw-expander-row.d.ts +7 -1
- package/lib/types/elements/adw-header-bar.d.ts +15 -0
- package/lib/types/elements/adw-icon.d.ts +25 -0
- package/lib/types/elements/adw-inline-view-switcher.d.ts +37 -11
- package/lib/types/elements/adw-menu-button.d.ts +6 -6
- package/lib/types/elements/adw-navigation-split-view.d.ts +6 -0
- package/lib/types/elements/adw-navigation-view.d.ts +63 -20
- package/lib/types/elements/adw-overlay-split-view.d.ts +38 -0
- package/lib/types/elements/adw-password-entry-row.d.ts +19 -10
- package/lib/types/elements/adw-popover.d.ts +58 -0
- package/lib/types/elements/adw-preferences-dialog.d.ts +57 -2
- package/lib/types/elements/adw-preferences-group.d.ts +31 -0
- package/lib/types/elements/adw-progress-bar.d.ts +38 -0
- package/lib/types/elements/adw-sidebar.d.ts +37 -5
- package/lib/types/elements/adw-split-button.d.ts +53 -7
- package/lib/types/elements/adw-switch-row.d.ts +11 -2
- package/lib/types/elements/adw-switch.d.ts +15 -0
- package/lib/types/elements/adw-tab-view.d.ts +129 -12
- package/lib/types/elements/adw-toolbar-view.d.ts +5 -0
- package/lib/types/elements/adw-view-stack.d.ts +27 -19
- package/lib/types/elements/adw-view-switcher-bar.d.ts +23 -7
- package/lib/types/elements/adw-view-switcher.d.ts +22 -11
- package/lib/types/elements/adw-window-title.d.ts +3 -1
- package/lib/types/elements/view-switcher-dom.d.ts +44 -0
- package/lib/types/entry-rows.spec.d.ts +1 -0
- package/lib/types/index.d.ts +7 -0
- package/lib/types/navigation-view.spec.d.ts +1 -0
- package/lib/types/preferences-search.d.ts +20 -0
- package/lib/types/preferences.spec.d.ts +1 -0
- package/lib/types/split-button.spec.d.ts +1 -0
- package/lib/types/split-views.spec.d.ts +1 -0
- package/lib/types/styles.generated.d.ts +1 -1
- package/lib/types/tab-view.spec.d.ts +1 -0
- package/lib/types/view-stack.spec.d.ts +1 -0
- package/lib/types/view-switcher.spec.d.ts +1 -0
- package/package.json +6 -6
- package/scss/_banner.scss +13 -0
- package/scss/_bottom_sheet.scss +5 -6
- package/scss/_button.scss +3 -26
- package/scss/_button_content.scss +28 -0
- package/scss/_checks.scss +228 -0
- package/scss/_clamp.scss +9 -3
- package/scss/_data_grid.scss +15 -7
- package/scss/_drop_down.scss +12 -55
- package/scss/_expander_row.scss +5 -50
- package/scss/_icon.scss +51 -0
- package/scss/_icons.generated.scss +6 -0
- package/scss/_inline_view_switcher.scss +34 -0
- package/scss/_labels.scss +135 -0
- package/scss/_menu_button.scss +10 -70
- package/scss/_popover.scss +186 -0
- package/scss/_preferences.scss +29 -0
- package/scss/_preferences_dialog.scss +17 -0
- package/scss/_progress_bar.scss +161 -0
- package/scss/_reset.scss +18 -7
- package/scss/_spinner.scss +7 -3
- package/scss/_split_button.scss +6 -50
- package/scss/_switch.scss +90 -0
- package/scss/_switch_row.scss +6 -52
- package/scss/_tab_view.scss +64 -0
- package/scss/_theme.scss +10 -0
- package/scss/_toolbar_view.scss +74 -0
- package/scss/_variables.scss +34 -0
- package/scss/_view_switcher.scss +40 -0
- package/scss/_view_switcher_bar.scss +37 -0
- package/scss/_wrap_box.scss +56 -6
- package/scss/adwaita-skin.scss +23 -0
- package/src/adw-about-dialog.spec.ts +350 -0
- package/src/adw-action-rows.spec.ts +342 -0
- package/src/adw-avatar.spec.ts +139 -0
- package/src/adw-banner.spec.ts +152 -0
- package/src/adw-button-content.spec.ts +183 -0
- package/src/adw-button-row.spec.ts +12 -6
- package/src/adw-carousel.spec.ts +389 -0
- package/src/adw-checks.spec.ts +368 -0
- package/src/adw-drop-down.spec.ts +123 -0
- package/src/adw-header-bar.spec.ts +83 -0
- package/src/adw-icon.spec.ts +167 -0
- package/src/adw-progress-bar.spec.ts +173 -0
- package/src/adw-sidebar.spec.ts +332 -0
- package/src/adw-switch.spec.ts +242 -0
- package/src/adw-tab-view.spec.ts +12 -3
- package/src/adw-view-switcher-bar.spec.ts +61 -13
- package/src/adw-wrap-box.spec.ts +194 -0
- package/src/bottom-sheet.spec.ts +325 -0
- package/src/breakpoints.spec.ts +147 -0
- package/src/breakpoints.ts +67 -0
- package/src/chrome.spec.ts +301 -0
- package/src/elements/adw-about-dialog.ts +223 -163
- package/src/elements/adw-action-row.ts +120 -10
- package/src/elements/adw-avatar.ts +48 -54
- package/src/elements/adw-banner.ts +46 -10
- package/src/elements/adw-bottom-sheet.ts +145 -60
- package/src/elements/adw-button-content.ts +107 -33
- package/src/elements/adw-button-row.ts +61 -37
- package/src/elements/adw-button.ts +5 -7
- package/src/elements/adw-carousel.ts +467 -105
- package/src/elements/adw-checks.ts +299 -0
- package/src/elements/adw-clamp.ts +80 -10
- package/src/elements/adw-combo-row.ts +15 -7
- package/src/elements/adw-data-grid.ts +43 -91
- package/src/elements/adw-drop-down.ts +117 -128
- package/src/elements/adw-entry-row.ts +404 -36
- package/src/elements/adw-expander-row.ts +61 -29
- package/src/elements/adw-header-bar.ts +31 -6
- package/src/elements/adw-icon.ts +148 -0
- package/src/elements/adw-inline-view-switcher.ts +240 -129
- package/src/elements/adw-menu-button.ts +93 -70
- package/src/elements/adw-navigation-split-view.ts +48 -7
- package/src/elements/adw-navigation-view.ts +286 -148
- package/src/elements/adw-overlay-split-view.ts +164 -14
- package/src/elements/adw-password-entry-row.ts +111 -89
- package/src/elements/adw-popover.ts +283 -0
- package/src/elements/adw-preferences-dialog.ts +120 -21
- package/src/elements/adw-preferences-group.ts +128 -22
- package/src/elements/adw-progress-bar.ts +189 -0
- package/src/elements/adw-sidebar.ts +301 -142
- package/src/elements/adw-spin-row.ts +11 -5
- package/src/elements/adw-spinner.ts +23 -8
- package/src/elements/adw-split-button.ts +371 -92
- package/src/elements/adw-status-page.ts +9 -9
- package/src/elements/adw-switch-row.ts +100 -30
- package/src/elements/adw-switch.ts +129 -0
- package/src/elements/adw-tab-view.ts +682 -129
- package/src/elements/adw-toggle-group.ts +5 -7
- package/src/elements/adw-toolbar-view.ts +84 -21
- package/src/elements/adw-view-stack.ts +162 -85
- package/src/elements/adw-view-switcher-bar.ts +163 -81
- package/src/elements/adw-view-switcher.ts +231 -112
- package/src/elements/adw-window-title.ts +44 -11
- package/src/elements/adw-wrap-box.ts +131 -35
- package/src/elements/view-switcher-dom.ts +108 -0
- package/src/entry-rows.spec.ts +391 -0
- package/src/index.ts +23 -0
- package/src/navigation-view.spec.ts +435 -0
- package/src/preferences-search.ts +135 -0
- package/src/preferences.spec.ts +250 -0
- package/src/source-view/adw-source-view.ts +2 -5
- package/src/split-button.spec.ts +470 -0
- package/src/split-views.spec.ts +309 -0
- package/src/styles.generated.ts +1 -1
- package/src/tab-view.spec.ts +451 -0
- package/src/test.browser.mts +49 -0
- package/src/view-stack.spec.ts +272 -0
- package/src/view-switcher.spec.ts +446 -0
|
@@ -1,25 +1,39 @@
|
|
|
1
1
|
// <adw-about-dialog> — The standard Adwaita "About" dialog (the web counterpart
|
|
2
2
|
// of Adw.AboutDialog). It is a modal AdwDialog (a full-cover scrim + a centred,
|
|
3
3
|
// scrollable floating sheet) hosting a navigation view: the main page shows the
|
|
4
|
-
// app icon, name, developer name and a version pill,
|
|
5
|
-
//
|
|
4
|
+
// app icon, name, developer name and a version pill, followed by up to three
|
|
5
|
+
// preference groups of rows that link out or push a subpage.
|
|
6
6
|
//
|
|
7
7
|
// Like Adw.AboutDialog the dialog is presented imperatively — `present()` (or
|
|
8
8
|
// setting the `open` attribute) reveals it; Escape, a scrim click, the header
|
|
9
9
|
// close button, or `close()` dismisses it. The header title fades in once the
|
|
10
10
|
// main page is scrolled (mirroring AdwAboutDialog's update_headerbar_cb).
|
|
11
11
|
//
|
|
12
|
+
// WHICH PAGE A ROW IS ON IS NOT A LAYOUT CHOICE — it is `update_details` and
|
|
13
|
+
// `update_support`, and this element used to get it wrong in three ways: it put
|
|
14
|
+
// Support Questions and Report an Issue on the DETAILS page, where the template
|
|
15
|
+
// keeps them on the MAIN page (adw-about-dialog.ui:146-186); it treated
|
|
16
|
+
// `website` as Details content, which is the one input libadwaita deliberately
|
|
17
|
+
// leaves out of that predicate (adw-about-dialog.c:1108), so a dialog whose only
|
|
18
|
+
// extra was a website grew a Details subpage holding a single link; and it
|
|
19
|
+
// invented fallbacks GTK does not have — "Application" for a missing name, a
|
|
20
|
+
// generic executable glyph for a missing icon, and an "About <app>" title on top
|
|
21
|
+
// of an "About <app>" label. All four derivations now come from
|
|
22
|
+
// `@gjsify/adwaita-core`, which holds them once for both renderers (ADR 0004)
|
|
23
|
+
// and is held to `@gjsify/adwaita-core/conformance`.
|
|
24
|
+
//
|
|
12
25
|
// Properties / attributes (all reflected, mirroring the AdwAboutDialog GObject
|
|
13
26
|
// properties of the same name, hyphenated):
|
|
14
27
|
// application-name the application's display name (shown as a title-1)
|
|
15
28
|
// application-icon a symbolic icon name (with or without -symbolic) — the
|
|
16
|
-
// large app icon;
|
|
29
|
+
// large app icon; absent one, no icon is drawn
|
|
17
30
|
// developer-name the developer / vendor line below the name
|
|
18
31
|
// version the version, shown as a clickable pill
|
|
19
32
|
// comments a longer description (shown on the Details page)
|
|
20
|
-
// website the application's website URL
|
|
21
|
-
//
|
|
22
|
-
//
|
|
33
|
+
// website the application's website URL — a Website link row, on
|
|
34
|
+
// the MAIN page unless the Details page has other content
|
|
35
|
+
// support-url a support-forum URL (a main-page Support Questions row)
|
|
36
|
+
// issue-url an issue-tracker URL (a main-page Report an Issue row)
|
|
23
37
|
// copyright the copyright line (shown on the Legal page)
|
|
24
38
|
// license the license text/name (shown on the Legal page); when a
|
|
25
39
|
// `license-url` is also given the name becomes a link
|
|
@@ -29,14 +43,23 @@
|
|
|
29
43
|
// List-valued credits are set via properties (string arrays), mirroring the
|
|
30
44
|
// AdwAboutDialog `developers` / `designers` / `artists` / `documenters`
|
|
31
45
|
// properties — entries may be `Name`, `Name <email>` or `Name https://url`,
|
|
32
|
-
//
|
|
46
|
+
// parsed by the core's `parseCreditPerson`, which is `parse_person` (:490-534).
|
|
47
|
+
//
|
|
48
|
+
// NOT implemented here (and therefore left at their empty values when the core
|
|
49
|
+
// derivations are asked): `release-notes` + the What's New page, `debug-info` +
|
|
50
|
+
// the Troubleshooting page, `add_link`, `add_credit_section`,
|
|
51
|
+
// `translator-credits` and the Acknowledgements page. The core covers all of
|
|
52
|
+
// them; adopting them is a feature, not a fidelity fix.
|
|
33
53
|
//
|
|
34
54
|
// Events (CustomEvent, bubbles):
|
|
35
55
|
// `notify::open` (detail = { open }) when the presented state changes —
|
|
36
56
|
// mirrors the Adw.Dialog `open` notification.
|
|
37
57
|
// `closed` when the dialog is dismissed — mirrors Adw.Dialog::closed.
|
|
38
|
-
// `activate-link` (detail = { uri }) when a link
|
|
39
|
-
// AdwAboutDialog::activate-link
|
|
58
|
+
// `activate-link` (detail = { uri }, CANCELABLE) when a link is activated —
|
|
59
|
+
// mirrors AdwAboutDialog::activate-link, a boolean signal with the
|
|
60
|
+
// `true_handled` accumulator (:2093-2103): a handler that returns TRUE
|
|
61
|
+
// suppresses the default navigation (:429-437). `preventDefault()` is the
|
|
62
|
+
// web spelling of returning TRUE.
|
|
40
63
|
//
|
|
41
64
|
// Reference: refs/libadwaita/src/adw-about-dialog.c (AdwAboutDialog behaviour)
|
|
42
65
|
// Reference: refs/libadwaita/src/adw-about-dialog.ui (navigation page tree)
|
|
@@ -44,30 +67,24 @@
|
|
|
44
67
|
// Reference: refs/libadwaita/src/stylesheet/widgets/_dialogs.scss (floating sheet)
|
|
45
68
|
// Reference: refs/adwaita-web/adwaita-web/scss/_about_dialog.scss (web layout)
|
|
46
69
|
// Copyright (c) 2022-2024 GNOME Foundation Inc. / Purism SPC (libadwaita). LGPLv2.1+.
|
|
47
|
-
// Modifications: Implemented as a Web Component for @gjsify/adwaita-web
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
// `Name https://url` — a trailing bare URL is the link target.
|
|
66
|
-
const url = /^(.*?)\s+((?:https?|mailto):\S+)$/.exec(trimmed);
|
|
67
|
-
if (url) {
|
|
68
|
-
return { name: url[1].trim(), uri: url[2].trim() };
|
|
69
|
-
}
|
|
70
|
-
return { name: trimmed, uri: null };
|
|
70
|
+
// Modifications: Implemented as a Web Component for @gjsify/adwaita-web; the
|
|
71
|
+
// icon nodes are <adw-icon>; the derivations live in @gjsify/adwaita-core.
|
|
72
|
+
|
|
73
|
+
import {
|
|
74
|
+
ADW_ABOUT_DIALOG_LABELS,
|
|
75
|
+
type AdwCreditPerson,
|
|
76
|
+
GTK_LICENSE,
|
|
77
|
+
aboutDialogVisibility,
|
|
78
|
+
creditsSections,
|
|
79
|
+
legalSectionVisible,
|
|
80
|
+
stripMnemonic,
|
|
81
|
+
} from '@gjsify/adwaita-core';
|
|
82
|
+
|
|
83
|
+
import { createAdwIcon } from './adw-icon.js';
|
|
84
|
+
|
|
85
|
+
/** A template label with its mnemonic marker removed — this renderer has no accelerator layer. */
|
|
86
|
+
function rowLabel(key: keyof typeof ADW_ABOUT_DIALOG_LABELS): string {
|
|
87
|
+
return stripMnemonic(ADW_ABOUT_DIALOG_LABELS[key]);
|
|
71
88
|
}
|
|
72
89
|
|
|
73
90
|
export class AdwAboutDialog extends HTMLElement {
|
|
@@ -265,6 +282,10 @@ export class AdwAboutDialog extends HTMLElement {
|
|
|
265
282
|
this._sheetEl.className = 'adw-about-dialog-sheet';
|
|
266
283
|
this._sheetEl.setAttribute('role', 'dialog');
|
|
267
284
|
this._sheetEl.setAttribute('aria-modal', 'true');
|
|
285
|
+
// AdwDialog:title, whose template default is the bare word (.ui:6). The
|
|
286
|
+
// application name is announced by the title-1 label inside, exactly as
|
|
287
|
+
// the header revealer shows it separately in GTK (.ui:29-31).
|
|
288
|
+
this._sheetEl.setAttribute('aria-label', ADW_ABOUT_DIALOG_LABELS.dialogTitle);
|
|
268
289
|
|
|
269
290
|
this.replaceChildren(this._scrimEl, this._sheetEl);
|
|
270
291
|
|
|
@@ -305,23 +326,56 @@ export class AdwAboutDialog extends HTMLElement {
|
|
|
305
326
|
}
|
|
306
327
|
}
|
|
307
328
|
|
|
329
|
+
/** The credit groups the Credits page shows, straight from `update_credits`. */
|
|
330
|
+
private _creditsSections() {
|
|
331
|
+
return creditsSections({
|
|
332
|
+
developers: this._developers,
|
|
333
|
+
designers: this._designers,
|
|
334
|
+
artists: this._artists,
|
|
335
|
+
documenters: this._documenters,
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Whether the Legal page has anything on it.
|
|
341
|
+
*
|
|
342
|
+
* This element's licence model is the simplified one — a name plus an
|
|
343
|
+
* optional URL — so it maps onto `GtkLicense` as "custom text when a licence
|
|
344
|
+
* is set, unknown otherwise", which is what `adw_about_dialog_set_license`
|
|
345
|
+
* does to the type anyway (:3472).
|
|
346
|
+
*/
|
|
347
|
+
private _hasLegal(): boolean {
|
|
348
|
+
const licenseType = this.license.length > 0 ? GTK_LICENSE.CUSTOM : GTK_LICENSE.UNKNOWN;
|
|
349
|
+
return legalSectionVisible(this.copyright, licenseType, this.license);
|
|
350
|
+
}
|
|
351
|
+
|
|
308
352
|
/** Rebuild the sheet contents from the current attributes / credit lists. */
|
|
309
353
|
private _render(): void {
|
|
310
|
-
const
|
|
311
|
-
this
|
|
354
|
+
const sections = this._creditsSections();
|
|
355
|
+
// The four inputs this element has no API for stay empty: `debug-info`,
|
|
356
|
+
// `release-notes`, `add_link` and the acknowledgements page.
|
|
357
|
+
const visibility = aboutDialogVisibility({
|
|
358
|
+
applicationIcon: this.applicationIcon,
|
|
359
|
+
applicationName: this.applicationName,
|
|
360
|
+
developerName: this.developerName,
|
|
361
|
+
version: this.version,
|
|
362
|
+
comments: this.comments,
|
|
363
|
+
website: this.website,
|
|
364
|
+
supportUrl: this.supportUrl,
|
|
365
|
+
issueUrl: this.issueUrl,
|
|
366
|
+
hasCredits: sections.length > 0,
|
|
367
|
+
hasLegal: this._hasLegal(),
|
|
368
|
+
});
|
|
312
369
|
|
|
313
370
|
// The navigation view (main page + subpages) lives inside the sheet.
|
|
314
371
|
this._navView = document.createElement('adw-navigation-view');
|
|
315
|
-
this._navView.append(this._buildMainPage(
|
|
316
|
-
|
|
317
|
-
const detailsPage = this._buildDetailsPage();
|
|
318
|
-
if (detailsPage) this._navView.append(detailsPage);
|
|
319
|
-
|
|
320
|
-
const creditsPage = this._buildCreditsPage();
|
|
321
|
-
if (creditsPage) this._navView.append(creditsPage);
|
|
372
|
+
this._navView.append(this._buildMainPage(visibility));
|
|
322
373
|
|
|
323
|
-
|
|
324
|
-
|
|
374
|
+
// The Details page exists exactly when its row does — `details_row` is
|
|
375
|
+
// `has_comments || show_links` (:1115), i.e. "the page has content".
|
|
376
|
+
if (visibility.detailsRow) this._navView.append(this._buildDetailsPage(visibility));
|
|
377
|
+
if (visibility.creditsRow) this._navView.append(this._buildCreditsPage(sections));
|
|
378
|
+
if (visibility.legalRow) this._navView.append(this._buildLegalPage());
|
|
325
379
|
|
|
326
380
|
this._sheetEl.replaceChildren(this._navView);
|
|
327
381
|
this._sheetEl.tabIndex = -1;
|
|
@@ -329,11 +383,18 @@ export class AdwAboutDialog extends HTMLElement {
|
|
|
329
383
|
this.classList.toggle('open', this.open);
|
|
330
384
|
}
|
|
331
385
|
|
|
332
|
-
/**
|
|
386
|
+
/**
|
|
387
|
+
* A navigation page wrapping a header bar (with the close button) + content.
|
|
388
|
+
*
|
|
389
|
+
* `headerTitle` defaults to the page title; the main page overrides it,
|
|
390
|
+
* because its page title is the dialog's ("About", .ui:19) while its header
|
|
391
|
+
* shows the application name (.ui:30).
|
|
392
|
+
*/
|
|
333
393
|
private _buildPage(
|
|
334
394
|
tag: string,
|
|
335
395
|
title: string,
|
|
336
396
|
showClose: boolean,
|
|
397
|
+
headerTitle: string = title,
|
|
337
398
|
): {
|
|
338
399
|
page: HTMLElement;
|
|
339
400
|
body: HTMLDivElement;
|
|
@@ -346,7 +407,7 @@ export class AdwAboutDialog extends HTMLElement {
|
|
|
346
407
|
const header = document.createElement('adw-header-bar');
|
|
347
408
|
// The main page's title is hidden until the page is scrolled (CSS), the
|
|
348
409
|
// subpages always show it (AdwAboutDialog update_headerbar_cb).
|
|
349
|
-
header.setAttribute('title',
|
|
410
|
+
header.setAttribute('title', headerTitle);
|
|
350
411
|
|
|
351
412
|
if (showClose) {
|
|
352
413
|
// The close button — drawn with a CSS "×" glyph (no window-close
|
|
@@ -392,27 +453,30 @@ export class AdwAboutDialog extends HTMLElement {
|
|
|
392
453
|
return { page, body, header };
|
|
393
454
|
}
|
|
394
455
|
|
|
395
|
-
/** The main page: icon, name, developer, version pill,
|
|
396
|
-
private _buildMainPage(
|
|
397
|
-
const { page, body } = this._buildPage('main',
|
|
456
|
+
/** The main page: icon, name, developer, version pill, then the three groups. */
|
|
457
|
+
private _buildMainPage(visibility: ReturnType<typeof aboutDialogVisibility>): HTMLElement {
|
|
458
|
+
const { page, body } = this._buildPage('main', ADW_ABOUT_DIALOG_LABELS.dialogTitle, true, this.applicationName);
|
|
398
459
|
|
|
399
|
-
// App icon — a large symbolic glyph
|
|
400
|
-
//
|
|
401
|
-
//
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
460
|
+
// App icon — a large symbolic glyph, and only when one was given. GTK
|
|
461
|
+
// hides the image outright for an empty icon name (:2313-2314); picking
|
|
462
|
+
// a generic glyph here would show an icon for an application that asked
|
|
463
|
+
// for none. A reverse-DNS application id (`org.gnome.Builder`) is not one
|
|
464
|
+
// CSS token and resolves to no glyph rather than to three stray classes —
|
|
465
|
+
// see `normalizeIconName`.
|
|
466
|
+
if (visibility.appIcon) {
|
|
467
|
+
body.appendChild(createAdwIcon(this.applicationIcon, 'adw-about-dialog-icon'));
|
|
468
|
+
}
|
|
407
469
|
|
|
408
|
-
// App name — title-1.
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
470
|
+
// App name — title-1. No fallback: an unset name means no label (:2376-2377).
|
|
471
|
+
if (visibility.appName) {
|
|
472
|
+
const nameEl = document.createElement('span');
|
|
473
|
+
nameEl.className = 'adw-about-dialog-name';
|
|
474
|
+
nameEl.textContent = this.applicationName;
|
|
475
|
+
body.appendChild(nameEl);
|
|
476
|
+
}
|
|
413
477
|
|
|
414
478
|
// Developer / vendor.
|
|
415
|
-
if (
|
|
479
|
+
if (visibility.developerName) {
|
|
416
480
|
const devEl = document.createElement('span');
|
|
417
481
|
devEl.className = 'adw-about-dialog-developer';
|
|
418
482
|
devEl.textContent = this.developerName;
|
|
@@ -420,52 +484,72 @@ export class AdwAboutDialog extends HTMLElement {
|
|
|
420
484
|
}
|
|
421
485
|
|
|
422
486
|
// Version pill.
|
|
423
|
-
if (
|
|
487
|
+
if (visibility.version) {
|
|
424
488
|
const versionEl = document.createElement('span');
|
|
425
489
|
versionEl.className = 'adw-about-dialog-version';
|
|
426
490
|
versionEl.textContent = this.version;
|
|
427
491
|
body.appendChild(versionEl);
|
|
428
492
|
}
|
|
429
493
|
|
|
430
|
-
//
|
|
431
|
-
//
|
|
432
|
-
//
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
group.appendChild(this._buildNavRow('Details', 'details'));
|
|
438
|
-
hasRows = true;
|
|
494
|
+
// Three separate preference groups, as in the template: details_group
|
|
495
|
+
// (.ui:96), support_group (.ui:146) and credits_legal_group (.ui:188).
|
|
496
|
+
// They are groups, not one list, and the stylesheet already spaces
|
|
497
|
+
// consecutive `.adw-about-dialog-rows` apart for exactly this.
|
|
498
|
+
const detailsGroupRows: HTMLElement[] = [];
|
|
499
|
+
if (visibility.detailsRow) {
|
|
500
|
+
detailsGroupRows.push(this._buildNavRow(rowLabel('detailsRow'), 'details'));
|
|
439
501
|
}
|
|
440
|
-
if (
|
|
441
|
-
|
|
442
|
-
hasRows = true;
|
|
443
|
-
}
|
|
444
|
-
if (this._hasLegal()) {
|
|
445
|
-
group.appendChild(this._buildNavRow('Legal', 'legal'));
|
|
446
|
-
hasRows = true;
|
|
502
|
+
if (visibility.websiteRow) {
|
|
503
|
+
detailsGroupRows.push(this._buildLinkRow(rowLabel('websiteRow'), this.website));
|
|
447
504
|
}
|
|
448
505
|
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
506
|
+
const supportGroupRows: HTMLElement[] = [];
|
|
507
|
+
if (visibility.supportRow) {
|
|
508
|
+
supportGroupRows.push(this._buildLinkRow(rowLabel('supportRow'), this.supportUrl));
|
|
509
|
+
}
|
|
510
|
+
if (visibility.issueRow) {
|
|
511
|
+
supportGroupRows.push(this._buildLinkRow(rowLabel('issueRow'), this.issueUrl));
|
|
454
512
|
}
|
|
455
513
|
|
|
514
|
+
const creditsLegalRows: HTMLElement[] = [];
|
|
515
|
+
if (visibility.creditsRow) creditsLegalRows.push(this._buildNavRow(rowLabel('creditsRow'), 'credits'));
|
|
516
|
+
if (visibility.legalRow) creditsLegalRows.push(this._buildNavRow(rowLabel('legalRow'), 'legal'));
|
|
517
|
+
|
|
518
|
+
if (visibility.detailsGroup) body.appendChild(this._buildGroup('details', detailsGroupRows));
|
|
519
|
+
if (visibility.supportGroup) body.appendChild(this._buildGroup('support', supportGroupRows));
|
|
520
|
+
if (visibility.creditsLegalGroup) body.appendChild(this._buildGroup('credits-legal', creditsLegalRows));
|
|
521
|
+
|
|
456
522
|
return page;
|
|
457
523
|
}
|
|
458
524
|
|
|
525
|
+
/**
|
|
526
|
+
* One `AdwPreferencesGroup` in its spacing wrapper.
|
|
527
|
+
*
|
|
528
|
+
* `data-group` carries the template's widget id so a test — and a reader —
|
|
529
|
+
* can tell the four named groups apart; nothing styles off it. The credits
|
|
530
|
+
* sections pass `null`, because the template has no id for them: they are
|
|
531
|
+
* built one per section at runtime (:548-549).
|
|
532
|
+
*/
|
|
533
|
+
private _buildGroup(id: string | null, rows: HTMLElement[], title?: string): HTMLElement {
|
|
534
|
+
const group = document.createElement('adw-preferences-group');
|
|
535
|
+
if (id !== null) group.setAttribute('data-group', id);
|
|
536
|
+
if (title !== undefined) group.setAttribute('title', title);
|
|
537
|
+
for (const row of rows) group.appendChild(row);
|
|
538
|
+
|
|
539
|
+
const wrap = document.createElement('div');
|
|
540
|
+
wrap.className = 'adw-about-dialog-rows';
|
|
541
|
+
wrap.appendChild(group);
|
|
542
|
+
return wrap;
|
|
543
|
+
}
|
|
544
|
+
|
|
459
545
|
/** An activatable row that pushes a subpage, with a trailing go-next chevron. */
|
|
460
546
|
private _buildNavRow(title: string, tag: string): HTMLElement {
|
|
461
547
|
const row = document.createElement('adw-action-row');
|
|
462
548
|
row.setAttribute('title', title);
|
|
463
549
|
row.setAttribute('activatable', '');
|
|
464
550
|
|
|
465
|
-
const chevron =
|
|
551
|
+
const chevron = createAdwIcon('go-next', 'adw-about-dialog-chevron');
|
|
466
552
|
chevron.setAttribute('slot', 'suffix');
|
|
467
|
-
chevron.className = 'adw-icon adw-icon--go-next adw-about-dialog-chevron';
|
|
468
|
-
chevron.setAttribute('aria-hidden', 'true');
|
|
469
553
|
row.appendChild(chevron);
|
|
470
554
|
|
|
471
555
|
row.addEventListener('activated', () => {
|
|
@@ -476,47 +560,37 @@ export class AdwAboutDialog extends HTMLElement {
|
|
|
476
560
|
return row;
|
|
477
561
|
}
|
|
478
562
|
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
);
|
|
563
|
+
/**
|
|
564
|
+
* Emit `activate-link` and navigate unless a handler took over.
|
|
565
|
+
*
|
|
566
|
+
* The GObject signal is `BOOLEAN__STRING` with `g_signal_accumulator_true_handled`
|
|
567
|
+
* (:2093-2103) and its default handler returns `GDK_EVENT_PROPAGATE`
|
|
568
|
+
* (:455-460), i.e. "not handled, do the default". A handler returning TRUE
|
|
569
|
+
* suppresses it — which is what `preventDefault()` on a cancelable event
|
|
570
|
+
* means, and why `dispatchEvent`'s return value is the gate here.
|
|
571
|
+
*/
|
|
572
|
+
private _activateLink(uri: string): void {
|
|
573
|
+
const event = new CustomEvent('activate-link', { bubbles: true, cancelable: true, detail: { uri } });
|
|
574
|
+
if (this.dispatchEvent(event)) window.open(uri, '_blank', 'noopener,noreferrer');
|
|
490
575
|
}
|
|
491
576
|
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
577
|
+
/** The Details page: comments + the links group (website + future custom links). */
|
|
578
|
+
private _buildDetailsPage(visibility: ReturnType<typeof aboutDialogVisibility>): HTMLElement {
|
|
579
|
+
const { page, body } = this._buildPage('details', ADW_ABOUT_DIALOG_LABELS.detailsPage, false);
|
|
495
580
|
|
|
496
|
-
|
|
497
|
-
private _buildDetailsPage(): HTMLElement | null {
|
|
498
|
-
if (!this._hasDetails()) return null;
|
|
499
|
-
const { page, body } = this._buildPage('details', 'Details', false);
|
|
500
|
-
|
|
501
|
-
if (this.comments) {
|
|
581
|
+
if (visibility.commentsLabel) {
|
|
502
582
|
const comments = document.createElement('p');
|
|
503
583
|
comments.className = 'adw-about-dialog-comments';
|
|
504
584
|
comments.textContent = this.comments;
|
|
505
585
|
body.appendChild(comments);
|
|
506
586
|
}
|
|
507
587
|
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
const group = document.createElement('adw-preferences-group');
|
|
515
|
-
for (const [title, uri] of links) group.appendChild(this._buildLinkRow(title, uri));
|
|
516
|
-
const wrap = document.createElement('div');
|
|
517
|
-
wrap.className = 'adw-about-dialog-rows';
|
|
518
|
-
wrap.appendChild(group);
|
|
519
|
-
body.appendChild(wrap);
|
|
588
|
+
if (visibility.linksGroup) {
|
|
589
|
+
const rows: HTMLElement[] = [];
|
|
590
|
+
if (visibility.detailsWebsiteRow) {
|
|
591
|
+
rows.push(this._buildLinkRow(rowLabel('websiteRow'), this.website));
|
|
592
|
+
}
|
|
593
|
+
body.appendChild(this._buildGroup('links', rows));
|
|
520
594
|
}
|
|
521
595
|
|
|
522
596
|
return page;
|
|
@@ -529,73 +603,54 @@ export class AdwAboutDialog extends HTMLElement {
|
|
|
529
603
|
row.setAttribute('activatable', '');
|
|
530
604
|
row.classList.add('adw-about-dialog-link-row');
|
|
531
605
|
|
|
532
|
-
const chevron =
|
|
606
|
+
const chevron = createAdwIcon('go-next', 'adw-about-dialog-chevron');
|
|
533
607
|
chevron.setAttribute('slot', 'suffix');
|
|
534
|
-
chevron.className = 'adw-icon adw-icon--go-next adw-about-dialog-chevron';
|
|
535
|
-
chevron.setAttribute('aria-hidden', 'true');
|
|
536
608
|
row.appendChild(chevron);
|
|
537
609
|
|
|
538
|
-
row.addEventListener('activated', () =>
|
|
539
|
-
this.dispatchEvent(new CustomEvent('activate-link', { bubbles: true, detail: { uri } }));
|
|
540
|
-
window.open(uri, '_blank', 'noopener,noreferrer');
|
|
541
|
-
});
|
|
610
|
+
row.addEventListener('activated', () => this._activateLink(uri));
|
|
542
611
|
|
|
543
612
|
return row;
|
|
544
613
|
}
|
|
545
614
|
|
|
546
|
-
/** The Credits page:
|
|
547
|
-
private _buildCreditsPage(): HTMLElement
|
|
548
|
-
|
|
549
|
-
const { page, body } = this._buildPage('credits', 'Credits', false);
|
|
550
|
-
|
|
551
|
-
const sections: Array<[string, string[]]> = [
|
|
552
|
-
['Code by', this._developers],
|
|
553
|
-
['Design by', this._designers],
|
|
554
|
-
['Artwork by', this._artists],
|
|
555
|
-
['Documentation by', this._documenters],
|
|
556
|
-
];
|
|
615
|
+
/** The Credits page: one group per section the core assembled. */
|
|
616
|
+
private _buildCreditsPage(sections: ReturnType<typeof creditsSections>): HTMLElement {
|
|
617
|
+
const { page, body } = this._buildPage('credits', ADW_ABOUT_DIALOG_LABELS.creditsPage, false);
|
|
557
618
|
|
|
558
|
-
for (const
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
group.setAttribute('title', title);
|
|
562
|
-
for (const person of people) group.appendChild(this._buildCreditRow(person));
|
|
563
|
-
const wrap = document.createElement('div');
|
|
564
|
-
wrap.className = 'adw-about-dialog-rows';
|
|
565
|
-
wrap.appendChild(group);
|
|
566
|
-
body.appendChild(wrap);
|
|
619
|
+
for (const section of sections) {
|
|
620
|
+
const rows = section.people.map((person) => this._buildCreditRow(person));
|
|
621
|
+
body.appendChild(this._buildGroup(null, rows, section.title ?? undefined));
|
|
567
622
|
}
|
|
568
623
|
|
|
569
624
|
return page;
|
|
570
625
|
}
|
|
571
626
|
|
|
572
|
-
/**
|
|
573
|
-
|
|
574
|
-
|
|
627
|
+
/**
|
|
628
|
+
* A credit row — a plain title, or a link row when the entry carries a URI.
|
|
629
|
+
*
|
|
630
|
+
* The gate is `link !== null`, not the URI's truthiness: `"Ada <>"` parses to
|
|
631
|
+
* an EMPTY link, which is still a link in the C (`if (link)` tests the
|
|
632
|
+
* pointer, :562) and still produces an `AdwLinkRow`, to `mailto:`.
|
|
633
|
+
*/
|
|
634
|
+
private _buildCreditRow(person: AdwCreditPerson): HTMLElement {
|
|
575
635
|
const row = document.createElement('adw-action-row');
|
|
576
|
-
row.setAttribute('title', name);
|
|
636
|
+
row.setAttribute('title', person.name);
|
|
577
637
|
|
|
578
|
-
if (
|
|
638
|
+
if (person.link !== null) {
|
|
639
|
+
const uri = person.uri ?? '';
|
|
579
640
|
row.setAttribute('activatable', '');
|
|
580
641
|
row.classList.add('adw-about-dialog-link-row');
|
|
581
|
-
const chevron =
|
|
642
|
+
const chevron = createAdwIcon('go-next', 'adw-about-dialog-chevron');
|
|
582
643
|
chevron.setAttribute('slot', 'suffix');
|
|
583
|
-
chevron.className = 'adw-icon adw-icon--go-next adw-about-dialog-chevron';
|
|
584
|
-
chevron.setAttribute('aria-hidden', 'true');
|
|
585
644
|
row.appendChild(chevron);
|
|
586
|
-
row.addEventListener('activated', () =>
|
|
587
|
-
this.dispatchEvent(new CustomEvent('activate-link', { bubbles: true, detail: { uri } }));
|
|
588
|
-
window.open(uri, '_blank', 'noopener,noreferrer');
|
|
589
|
-
});
|
|
645
|
+
row.addEventListener('activated', () => this._activateLink(uri));
|
|
590
646
|
}
|
|
591
647
|
|
|
592
648
|
return row;
|
|
593
649
|
}
|
|
594
650
|
|
|
595
651
|
/** The Legal page: copyright + license text. */
|
|
596
|
-
private _buildLegalPage(): HTMLElement
|
|
597
|
-
|
|
598
|
-
const { page, body } = this._buildPage('legal', 'Legal', false);
|
|
652
|
+
private _buildLegalPage(): HTMLElement {
|
|
653
|
+
const { page, body } = this._buildPage('legal', ADW_ABOUT_DIALOG_LABELS.legalPage, false);
|
|
599
654
|
|
|
600
655
|
if (this.copyright) {
|
|
601
656
|
const copyright = document.createElement('p');
|
|
@@ -608,11 +663,16 @@ export class AdwAboutDialog extends HTMLElement {
|
|
|
608
663
|
const license = document.createElement('p');
|
|
609
664
|
license.className = 'adw-about-dialog-license';
|
|
610
665
|
if (this.licenseUrl) {
|
|
666
|
+
// GTK connects the legal label's `activate-link` to the same
|
|
667
|
+
// handler as the rows (:695), so this link is cancelable too.
|
|
611
668
|
const link = document.createElement('a');
|
|
612
669
|
link.href = this.licenseUrl;
|
|
613
|
-
link.target = '_blank';
|
|
614
670
|
link.rel = 'noopener noreferrer';
|
|
615
671
|
link.textContent = this.license;
|
|
672
|
+
link.addEventListener('click', (event) => {
|
|
673
|
+
event.preventDefault();
|
|
674
|
+
this._activateLink(this.licenseUrl);
|
|
675
|
+
});
|
|
616
676
|
license.appendChild(link);
|
|
617
677
|
} else {
|
|
618
678
|
license.textContent = this.license;
|