@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.
Files changed (146) hide show
  1. package/dist/adwaita-web.css +433 -33
  2. package/dist/adwaita-web.css.map +1 -1
  3. package/lib/types/adw-shortcut-label.spec.d.ts +2 -0
  4. package/lib/types/breakpoints.d.ts +10 -12
  5. package/lib/types/elements/adw-about-dialog.d.ts +19 -36
  6. package/lib/types/elements/adw-action-row.d.ts +9 -12
  7. package/lib/types/elements/adw-alert-dialog.d.ts +0 -12
  8. package/lib/types/elements/adw-avatar.d.ts +1 -0
  9. package/lib/types/elements/adw-bottom-sheet.d.ts +7 -12
  10. package/lib/types/elements/adw-button-content.d.ts +3 -4
  11. package/lib/types/elements/adw-button-row.d.ts +1 -1
  12. package/lib/types/elements/adw-carousel.d.ts +28 -49
  13. package/lib/types/elements/adw-checks.d.ts +2 -8
  14. package/lib/types/elements/adw-combo-row.d.ts +6 -6
  15. package/lib/types/elements/adw-data-grid.d.ts +5 -10
  16. package/lib/types/elements/adw-drop-down.d.ts +3 -11
  17. package/lib/types/elements/adw-entry-row.d.ts +20 -23
  18. package/lib/types/elements/adw-icon.d.ts +2 -4
  19. package/lib/types/elements/adw-inline-view-switcher.d.ts +18 -31
  20. package/lib/types/elements/adw-navigation-split-view.d.ts +7 -12
  21. package/lib/types/elements/adw-navigation-view.d.ts +2 -10
  22. package/lib/types/elements/adw-overlay-split-view.d.ts +28 -52
  23. package/lib/types/elements/adw-password-entry-row.d.ts +4 -5
  24. package/lib/types/elements/adw-popover.d.ts +2 -7
  25. package/lib/types/elements/adw-preferences-dialog.d.ts +21 -34
  26. package/lib/types/elements/adw-preferences-group.d.ts +12 -14
  27. package/lib/types/elements/adw-progress-bar.d.ts +3 -4
  28. package/lib/types/elements/adw-shortcut-label.d.ts +20 -0
  29. package/lib/types/elements/adw-sidebar.d.ts +5 -10
  30. package/lib/types/elements/adw-spinner.d.ts +3 -5
  31. package/lib/types/elements/adw-split-button.d.ts +6 -16
  32. package/lib/types/elements/adw-switch-row.d.ts +3 -3
  33. package/lib/types/elements/adw-tab-view.d.ts +16 -26
  34. package/lib/types/elements/adw-toast-overlay.d.ts +8 -13
  35. package/lib/types/elements/adw-view-stack.d.ts +7 -17
  36. package/lib/types/elements/adw-view-switcher-bar.d.ts +4 -8
  37. package/lib/types/elements/adw-view-switcher.d.ts +18 -38
  38. package/lib/types/elements/adw-wrap-box.d.ts +7 -17
  39. package/lib/types/elements/view-switcher-dom.d.ts +10 -13
  40. package/lib/types/index.d.ts +1 -0
  41. package/lib/types/source-view/hex.d.ts +7 -13
  42. package/lib/types/style-classes.spec.d.ts +11 -0
  43. package/lib/types/styles.generated.d.ts +1 -1
  44. package/package.json +6 -6
  45. package/scss/_about_dialog.scss +3 -2
  46. package/scss/_action_row.scss +33 -0
  47. package/scss/_avatar.scss +12 -0
  48. package/scss/_button.scss +44 -22
  49. package/scss/_card.scss +11 -8
  50. package/scss/_checks.scss +2 -4
  51. package/scss/_dialog.scss +3 -2
  52. package/scss/_headerbar.scss +23 -0
  53. package/scss/_popover.scss +8 -4
  54. package/scss/_preferences.scss +7 -7
  55. package/scss/_progress_bar.scss +9 -6
  56. package/scss/_reset.scss +1 -1
  57. package/scss/_shortcut_label.scss +80 -0
  58. package/scss/_sidebar.scss +57 -0
  59. package/scss/_status_page.scss +20 -0
  60. package/scss/_style_classes.scss +407 -0
  61. package/scss/_switch.scss +4 -3
  62. package/scss/_variables.scss +25 -0
  63. package/scss/adwaita-skin.scss +12 -0
  64. package/src/adw-about-dialog.spec.ts +34 -49
  65. package/src/adw-action-rows.spec.ts +25 -33
  66. package/src/adw-avatar.spec.ts +51 -20
  67. package/src/adw-banner.spec.ts +5 -8
  68. package/src/adw-button-content.spec.ts +9 -12
  69. package/src/adw-button-row.spec.ts +2 -4
  70. package/src/adw-carousel.spec.ts +40 -53
  71. package/src/adw-checks.spec.ts +36 -47
  72. package/src/adw-drop-down.spec.ts +25 -32
  73. package/src/adw-header-bar.spec.ts +5 -7
  74. package/src/adw-icon.spec.ts +23 -32
  75. package/src/adw-progress-bar.spec.ts +10 -12
  76. package/src/adw-row-state.spec.ts +9 -10
  77. package/src/adw-shortcut-label.spec.ts +112 -0
  78. package/src/adw-sidebar.spec.ts +17 -24
  79. package/src/adw-switch.spec.ts +11 -17
  80. package/src/adw-tab-view.spec.ts +7 -10
  81. package/src/adw-toast-overlay.spec.ts +7 -11
  82. package/src/adw-view-switcher-bar.spec.ts +10 -14
  83. package/src/adw-wrap-box.spec.ts +25 -36
  84. package/src/bottom-sheet.spec.ts +30 -36
  85. package/src/breakpoints.ts +15 -18
  86. package/src/chrome.spec.ts +13 -21
  87. package/src/elements/adw-about-dialog.ts +71 -114
  88. package/src/elements/adw-action-row.ts +22 -37
  89. package/src/elements/adw-alert-dialog.ts +25 -50
  90. package/src/elements/adw-avatar.ts +40 -15
  91. package/src/elements/adw-banner.ts +14 -18
  92. package/src/elements/adw-bottom-sheet.ts +31 -52
  93. package/src/elements/adw-button-content.ts +30 -37
  94. package/src/elements/adw-button-row.ts +14 -24
  95. package/src/elements/adw-carousel.ts +87 -148
  96. package/src/elements/adw-checks.ts +43 -56
  97. package/src/elements/adw-clamp.ts +8 -9
  98. package/src/elements/adw-combo-row.ts +17 -25
  99. package/src/elements/adw-data-grid.ts +26 -38
  100. package/src/elements/adw-dialog.ts +25 -56
  101. package/src/elements/adw-drop-down.ts +31 -51
  102. package/src/elements/adw-entry-row.ts +39 -46
  103. package/src/elements/adw-expander-row.ts +30 -53
  104. package/src/elements/adw-icon.ts +30 -53
  105. package/src/elements/adw-inline-view-switcher.ts +38 -67
  106. package/src/elements/adw-menu-button.ts +28 -58
  107. package/src/elements/adw-navigation-split-view.ts +35 -47
  108. package/src/elements/adw-navigation-view.ts +12 -28
  109. package/src/elements/adw-overlay-split-view.ts +100 -175
  110. package/src/elements/adw-password-entry-row.ts +18 -28
  111. package/src/elements/adw-popover.ts +28 -44
  112. package/src/elements/adw-preferences-dialog.ts +41 -66
  113. package/src/elements/adw-preferences-group.ts +33 -46
  114. package/src/elements/adw-progress-bar.ts +27 -47
  115. package/src/elements/adw-shortcut-label.ts +138 -0
  116. package/src/elements/adw-sidebar.ts +31 -39
  117. package/src/elements/adw-spin-row.ts +7 -14
  118. package/src/elements/adw-spinner.ts +31 -43
  119. package/src/elements/adw-split-button.ts +45 -75
  120. package/src/elements/adw-switch-row.ts +29 -46
  121. package/src/elements/adw-switch.ts +28 -47
  122. package/src/elements/adw-tab-view.ts +62 -103
  123. package/src/elements/adw-toast-overlay.ts +28 -35
  124. package/src/elements/adw-toggle-group.ts +9 -16
  125. package/src/elements/adw-view-stack.ts +32 -51
  126. package/src/elements/adw-view-switcher-bar.ts +26 -50
  127. package/src/elements/adw-view-switcher.ts +60 -110
  128. package/src/elements/adw-window-title.ts +4 -4
  129. package/src/elements/adw-wrap-box.ts +33 -59
  130. package/src/elements/view-switcher-dom.ts +15 -22
  131. package/src/entry-rows.spec.ts +18 -24
  132. package/src/index.ts +13 -15
  133. package/src/navigation-view.spec.ts +7 -10
  134. package/src/preferences-search.ts +11 -15
  135. package/src/preferences.spec.ts +13 -15
  136. package/src/source-view/adw-source-view.ts +6 -19
  137. package/src/source-view/hex.ts +7 -13
  138. package/src/split-button.spec.ts +32 -44
  139. package/src/split-views.spec.ts +83 -149
  140. package/src/style-classes.spec.ts +181 -0
  141. package/src/style-isolation.spec.ts +3 -6
  142. package/src/styles.generated.ts +1 -1
  143. package/src/tab-view.spec.ts +26 -35
  144. package/src/test.browser.mts +4 -0
  145. package/src/view-stack.spec.ts +23 -34
  146. package/src/view-switcher.spec.ts +56 -84
@@ -4,56 +4,37 @@
4
4
  // headerbar-colored strip, with the selected chip raised onto the view
5
5
  // background.
6
6
  //
7
- // The MODEL is HEADLESS and lives in `@gjsify/adwaita-core` (ADR 0004) as
8
- // {@link TabViewState}, shared with the NativeScript twin and specced by the
9
- // conformance vectors in `@gjsify/adwaita-core/conformance`. This element is the
10
- // DOM half only: it turns declared markup into pages, builds and MOVES tab
11
- // chips, toggles visibility, reflects the selection to an attribute, re-emits
12
- // changes as DOM events, and maps key chords onto the state machine.
7
+ // The MODEL is HEADLESS, in `@gjsify/adwaita-core`'s {@link TabViewState}
8
+ // (ADR 0004), shared with the NativeScript twin and specced by
9
+ // `@gjsify/adwaita-core/conformance`. This element is the DOM half only.
13
10
  //
14
- // What the lift fixed here, all of it C-derived:
15
- // - `close-page` removed NOTHING, so every close was permanently denied. The
16
- // default now closes a non-pinned page and denies a pinned one, exactly as
17
- // `close_page_cb` does (adw-tab-view.c:1986-1993), and the event is
18
- // CANCELABLE so `preventDefault()` defers the close until
19
- // `closePageFinish()` — the "save before closing?" seam that did not exist.
20
- // - `selected="99"` CLAMPED to the last page and `selected="-1"` to the first;
21
- // libadwaita ignores both (:2145, :4126-4134).
22
- // - `view.selected = 2` changed the visible page and fired nothing, while a
23
- // tab click fired; C notifies on every path (:1854).
24
- // - `<adw-tab-page>` declared `observedAttributes = ['title']` with no
25
- // `attributeChangedCallback` behind it, so the declaration was dead and a
26
- // title could never change. Titles, tooltips, icons and the loading state
27
- // are now live, as `AdwTab` keeps them (adw-tab.c:930-931).
28
- // - the bar hid on `pages <= 1` with no pinned clause, so a single PINNED tab
29
- // hid the bar where libadwaita keeps it (adw-tab-bar.c:163).
30
- // - the close button was always visible; it now follows `tabCloseVisible`
31
- // (adw-tab.c:124 + the pinned gate at :645-650).
32
- // - a roving tabindex was installed with NOTHING wired to move it, so every
33
- // inactive tab was keyboard-unreachable under role=tablist.
11
+ // The C rules that are not visible in the DOM code:
12
+ // - closing defaults to closing a non-pinned page and DENYING a pinned one
13
+ // (`close_page_cb`).
14
+ // - an out-of-range `selected` is IGNORED, not clamped, and every path notifies,
15
+ // property sets included.
16
+ // - the bar stays up for a single PINNED tab.
17
+ // - the close affordance follows `tabCloseVisible` (three terms plus a pinned
18
+ // gate), and titles/tooltips/icons/loading stay live.
19
+ //
20
+ // Pages are declared as <adw-tab-page> children — the element itself becomes the
21
+ // panel, so its attributes stay live — or added imperatively. The roving tabindex
22
+ // must keep moving: under `role=tablist` a frozen one leaves every inactive tab
23
+ // keyboard-unreachable.
24
+ //
25
+ // MODIFICATION: Adw.TabBar defaults `autohide` TRUE (`DEFAULT_TAB_AUTOHIDE`); an
26
+ // HTML boolean attribute cannot express a TRUE default, so `autohide` is opt-in
27
+ // here. `no-close` is web-specific, for static tab sets such as documentation
28
+ // command tabs.
29
+ //
30
+ // Events (all CustomEvent, all bubbling): `notify::selected-page`
31
+ // (`{ selected, selectedId, previousId, interactive }`); `close-page`
32
+ // (`{ index, id }`), CANCELABLE and one per close ATTEMPT, where
33
+ // `preventDefault()` holds the page open until `closePageFinish(id, confirm)`;
34
+ // and `page-attached` / `page-detached` / `page-reordered` / `page-pinned` /
35
+ // `page-updated` (`{ id, position, previousPosition }`), one per page-list change
36
+ // so a bound tab bar can move ONE chip instead of rebuilding.
34
37
  //
35
- // Pages are declared as <adw-tab-page> children (the element itself becomes the
36
- // page panel, so its attributes stay live) or added imperatively.
37
- // Attributes:
38
- // selected — zero-based index of the visible page; out-of-range is IGNORED.
39
- // autohide — hide the tab bar when it has nothing to show. NB Adw.TabBar
40
- // defaults this to TRUE (`DEFAULT_TAB_AUTOHIDE`); an HTML boolean attribute
41
- // cannot, so this stays opt-in — a deliberate, citable inversion.
42
- // expand-tabs — tabs stretch to fill the bar evenly (Adw.TabBar `expand-tabs`).
43
- // no-close — render no close affordance; web-specific escape hatch for
44
- // static tab sets such as documentation command tabs.
45
- // <adw-tab-page> attributes: page-id, title, tooltip, icon, indicator-icon,
46
- // loading, needs-attention, pinned.
47
- // Events:
48
- // `notify::selected-page` (CustomEvent, bubbles,
49
- // detail = { selected, selectedId, previousId, interactive }).
50
- // `close-page` (CustomEvent, bubbles, CANCELABLE,
51
- // detail = { index, id }) — one per close ATTEMPT. `preventDefault()` holds
52
- // the page open until `closePageFinish(id, confirm)`.
53
- // `page-attached` / `page-detached` / `page-reordered` / `page-pinned` /
54
- // `page-updated` (CustomEvent, bubbles,
55
- // detail = { id, position, previousPosition }) — one per page-list change,
56
- // so a bound tab bar can move ONE chip instead of rebuilding.
57
38
  // Reference: refs/libadwaita/src/adw-tab-view.c (AdwTabView behaviour)
58
39
  // Reference: refs/libadwaita/src/adw-tab-bar.c (AdwTabBar autohide)
59
40
  // Reference: refs/libadwaita/src/adw-tab.c (AdwTab chip)
@@ -69,20 +50,18 @@ import type { AdwTabPageSpec, AdwTabPageState, TabViewPagesChange, TabViewSelect
69
50
 
70
51
  import { type AdwIcon, createAdwIcon } from './adw-icon.js';
71
52
 
72
- /** A page descriptor as `pages` exposes it. */
73
53
  export type AdwTabViewPage = AdwTabPageState<HTMLElement>;
74
54
 
75
- /** The page spec the imperative insertion methods take. */
76
55
  export type AdwTabViewPageSpec = AdwTabPageSpec<HTMLElement>;
77
56
 
78
57
  /** The live `<adw-tab-page>` properties, each mapped onto a state setter. */
79
58
  const PAGE_ATTRIBUTES = ['title', 'tooltip', 'icon', 'indicator-icon', 'loading', 'needs-attention', 'pinned'];
80
59
 
81
60
  /**
82
- * A single page. Declared as a child of <adw-tab-view>; the element itself
83
- * becomes the page panel, so its attributes keep driving the tab after connect —
84
- * the old implementation moved the children out and discarded the element, which
85
- * is why its `observedAttributes` declaration had nothing to act on.
61
+ * A single page. Declared as a child of <adw-tab-view>; the element itself becomes the
62
+ * page panel, so its attributes keep driving the tab after connect — a renderer that
63
+ * moves the children out and discards the element leaves `observedAttributes` with
64
+ * nothing to act on.
86
65
  */
87
66
  export class AdwTabPage extends HTMLElement {
88
67
  static get observedAttributes() {
@@ -136,8 +115,6 @@ export class AdwTabView extends HTMLElement {
136
115
  this._state.subscribe((change) => this._onSelectionChange(change));
137
116
  }
138
117
 
139
- // --- Declarative surface ------------------------------------------------
140
-
141
118
  /** Zero-based index of the visible page, `-1` when the view is empty. */
142
119
  get selected(): number {
143
120
  return this._state.selectedIndex;
@@ -160,7 +137,6 @@ export class AdwTabView extends HTMLElement {
160
137
  else this.removeAttribute('autohide');
161
138
  }
162
139
 
163
- /** Whether tabs stretch to fill the bar evenly (Adw.TabBar `expand-tabs`). */
164
140
  get expandTabs(): boolean {
165
141
  return this.hasAttribute('expand-tabs');
166
142
  }
@@ -250,17 +226,14 @@ export class AdwTabView extends HTMLElement {
250
226
 
251
227
  // --- Model surface (thin delegations to TabViewState) --------------------
252
228
 
253
- /** All pages in order. */
254
229
  get pages(): readonly AdwTabViewPage[] {
255
230
  return this._state.pages;
256
231
  }
257
232
 
258
- /** Number of pages. */
259
233
  get nPages(): number {
260
234
  return this._state.nPages;
261
235
  }
262
236
 
263
- /** Length of the pinned prefix. */
264
237
  get nPinnedPages(): number {
265
238
  return this._state.nPinnedPages;
266
239
  }
@@ -275,7 +248,6 @@ export class AdwTabView extends HTMLElement {
275
248
  return this._state.selectedIndex;
276
249
  }
277
250
 
278
- /** The selected page's panel element, or `null`. */
279
251
  get selectedContent(): HTMLElement | null {
280
252
  return this._state.selectedPage?.content ?? null;
281
253
  }
@@ -426,8 +398,6 @@ export class AdwTabView extends HTMLElement {
426
398
  return this._state.diagnostics;
427
399
  }
428
400
 
429
- // --- Adoption -----------------------------------------------------------
430
-
431
401
  private _adoptDeclaredPage(pageEl: AdwTabPage): void {
432
402
  // The declared element IS the panel — attributes stay live on it, which
433
403
  // is what makes `<adw-tab-page title>` observable at all.
@@ -461,8 +431,6 @@ export class AdwTabView extends HTMLElement {
461
431
  return `tab-${this._generatedIds}`;
462
432
  }
463
433
 
464
- // --- Rendering ----------------------------------------------------------
465
-
466
434
  private _onPagesChange(change: TabViewPagesChange): void {
467
435
  switch (change.kind) {
468
436
  case 'attached': {
@@ -507,19 +475,16 @@ export class AdwTabView extends HTMLElement {
507
475
  tab.setAttribute('role', 'tab');
508
476
  tab.dataset.pageId = page.id;
509
477
 
510
- // Decorative mask-image nodes — <adw-icon> carries the convention (and
511
- // the `-symbolic` strip, which is that convention and not C: the name
512
- // reaches `GtkImage` untouched there).
478
+ // Decorative mask-image nodes — <adw-icon> carries the convention, including the
479
+ // `-symbolic` strip, which is ours and not C's: there the name reaches
480
+ // `GtkImage` untouched.
513
481
  const icon = createAdwIcon(null, 'adw-tab-icon');
514
482
  tab.appendChild(icon);
515
483
 
516
- // `AdwTabPage:loading` swaps the icon image for an `AdwSpinnerPaintable`
517
- // — the SAME paintable `Adw.Spinner` uses (`update_icons`,
518
- // adw-tab.c:172-190). So this is a real `<adw-spinner>` in the icon's
519
- // slot, not a ring drawn again in CSS: the stylesheet used to restate
520
- // the border, the grey and the 0.8s under a comment claiming it was
521
- // "not a second copy of them", and it inherited every spinner defect
522
- // independently.
484
+ // `AdwTabPage:loading` swaps the icon image for an `AdwSpinnerPaintable` — the
485
+ // SAME paintable `Adw.Spinner` uses (`update_icons`). So this is a real
486
+ // `<adw-spinner>` in the icon's slot, not a ring drawn again in CSS: a CSS copy
487
+ // inherits every spinner defect independently.
523
488
  const spinner = document.createElement('adw-spinner');
524
489
  spinner.className = 'adw-tab-spinner';
525
490
  spinner.setAttribute('size', '16');
@@ -532,9 +497,10 @@ export class AdwTabView extends HTMLElement {
532
497
 
533
498
  tab.appendChild(createAdwIcon(null, 'adw-tab-indicator'));
534
499
 
535
- // Close affordance — a small flat button drawn with a CSS "×" glyph (no
536
- // symbolic close icon ships in @gjsify/adwaita-icons). `can-focus=False`
537
- // in adw-tab.ui:72, so it stays out of the tab order here too.
500
+ // Close affordance — a small flat button drawn with a CSS "×" glyph, because
501
+ // `window-close` has no mask class: it is not in the ICONS map in
502
+ // `scripts/build-scss.mjs` that generates them. `can-focus=False` in C, so it
503
+ // stays out of the tab order here too.
538
504
  const close = document.createElement('button');
539
505
  close.type = 'button';
540
506
  close.className = 'adw-tab-close';
@@ -567,7 +533,6 @@ export class AdwTabView extends HTMLElement {
567
533
  this._refreshTab(page.id);
568
534
  }
569
535
 
570
- /** Remove-then-insert, the same shape the model's own reorder has. */
571
536
  private _moveTab(id: string, position: number): void {
572
537
  const tab = this._tabs.get(id);
573
538
  if (tab) {
@@ -588,10 +553,9 @@ export class AdwTabView extends HTMLElement {
588
553
 
589
554
  const label = tab.querySelector('.adw-tab-title') as HTMLElement | null;
590
555
  if (label) label.textContent = page.title;
591
- // The tooltip is Pango MARKUP when the page sets one of its own
592
- // (adw-tab.c:141-146). The DOM `title` attribute is a TEXT sink, so the
593
- // markup is shown verbatim rather than being pushed through an HTML sink
594
- // — a renderer that interpreted it would be executing page-supplied markup.
556
+ // The tooltip is Pango MARKUP when the page sets one of its own. The DOM `title`
557
+ // attribute is a TEXT sink, so the markup is shown verbatim rather than pushed
558
+ // through an HTML sink — interpreting it would execute page-supplied markup.
595
559
  tab.title = tabTooltip(page);
596
560
  tab.classList.toggle('pinned', page.pinned);
597
561
  tab.classList.toggle('needs-attention', page.needsAttention);
@@ -618,10 +582,10 @@ export class AdwTabView extends HTMLElement {
618
582
  }
619
583
 
620
584
  /**
621
- * `tabCloseVisible` (adw-tab.c:124 + the pinned gate at :645-650). `dragging`
622
- * is constantly false — tab drag-and-drop is compositor work and is not
623
- * modelled — and `fullyVisible` is measured against the bar's scroll window,
624
- * which is exactly what the C term means for a horizontally scrolled bar.
585
+ * `tabCloseVisible`, with its pinned gate. `dragging` is constantly false — tab
586
+ * drag-and-drop is compositor work and is not modelled — and `fullyVisible` is
587
+ * measured against the bar's scroll window, which is what the C term means for a
588
+ * horizontally scrolled bar.
625
589
  */
626
590
  private _refreshCloseVisibility(id: string): void {
627
591
  const page = this._state.getPage(id);
@@ -700,23 +664,18 @@ export class AdwTabView extends HTMLElement {
700
664
 
701
665
  private _applySelectedAttribute(value: string): void {
702
666
  const index = Number.parseInt(value, 10);
703
- // An unparseable value is IGNORED rather than treated as 0: the old
704
- // `Number.isNaN(raw) ? 0 : clamp(raw)` made `selected="oops"` select the
705
- // first page, and `selected="99"` the last, where libadwaita refuses both
706
- // (adw-tab-view.c:2145, :4126-4134).
667
+ // An unparseable value is IGNORED rather than treated as 0, and an out-of-range
668
+ // one is refused rather than clamped: libadwaita refuses both, so
669
+ // `selected="oops"` must not select the first page nor `selected="99"` the last.
707
670
  if (Number.isNaN(index)) return;
708
671
  this._state.selectNthPage(index);
709
672
  }
710
673
 
711
- // --- Keyboard -----------------------------------------------------------
712
-
713
674
  /**
714
- * The `Adw.TabView` shortcut table (`init_shortcuts`, adw-tab-view.c:2192+),
715
- * plus the ArrowLeft/ArrowRight movement `role=tablist` promises.
716
- *
717
- * Ctrl+Tab WRAPS and Ctrl+Page-Up/Down does not — the same `last` flag that
718
- * separates them in `select_page_cb` (:2017-2041). Alt+0 is page index 9,
719
- * because pages count from 0 (:2149-2150).
675
+ * The `Adw.TabView` shortcut table (`init_shortcuts`), plus the ArrowLeft/ArrowRight
676
+ * movement `role=tablist` promises. Ctrl+Tab WRAPS and Ctrl+Page-Up/Down does not —
677
+ * the same `last` flag that separates them in `select_page_cb`. Alt+0 is page index
678
+ * 9, because pages count from 0.
720
679
  */
721
680
  private _onKeyDown(event: KeyboardEvent): void {
722
681
  const inBar = event.target instanceof Node && this._barEl.contains(event.target);
@@ -758,10 +717,10 @@ export class AdwTabView extends HTMLElement {
758
717
  }
759
718
 
760
719
  /**
761
- * The `close-page` seam. A CANCELABLE DOM event is the idiomatic spelling of
762
- * the C signal's return value: letting it through takes libadwaita's default
763
- * (`!pinned`, adw-tab-view.c:1990-1991), and `preventDefault()` defers the
764
- * close until the app calls {@link closePageFinish}.
720
+ * The `close-page` seam. A CANCELABLE DOM event is the idiomatic spelling of the C
721
+ * signal's return value: letting it through takes libadwaita's default (`!pinned`),
722
+ * and `preventDefault()` defers the close until the app calls
723
+ * {@link closePageFinish}.
765
724
  */
766
725
  private _requestClose(page: AdwTabViewPage): boolean | 'defer' {
767
726
  const proceed = this.dispatchEvent(
@@ -1,15 +1,11 @@
1
- // <adw-toast-overlay> — Adwaita toast notification overlay.
2
- // The web counterpart of Adw.ToastOverlay: it wraps arbitrary content and shows
3
- // transient `.adw-toast` banners (the Adw.Toast equivalent — a title, an
4
- // optional action button and a close affordance) layered over the bottom of its
5
- // own bounds.
1
+ // <adw-toast-overlay> — the web counterpart of Adw.ToastOverlay: wraps arbitrary
2
+ // content and shows transient toast strips (title, optional action button, close
3
+ // affordance) layered over the bottom of its own bounds.
6
4
  //
7
- // The QUEUE — Adw.ToastOverlay's one-at-a-time policy, the FIFO ordering and the
8
- // auto-dismiss lifecycle — is HEADLESS and lives in `@gjsify/adwaita-core` (ADR
9
- // 0004) as {@link AdwToastQueue}; this element composes it and keeps only the DOM
10
- // render half: building the `.adw-toast` strip on `onShow`, tearing it down on
11
- // `onHide`, and the enter/exit CSS transitions. `@gjsify/adwaita-nativescript`
12
- // composes the same core queue, so both ports share one behaviour.
5
+ // The QUEUE — the one-at-a-time policy, FIFO ordering and auto-dismiss lifecycle —
6
+ // is HEADLESS and lives in `@gjsify/adwaita-core` (ADR 0004) as {@link AdwToastQueue};
7
+ // this element composes it and keeps only the DOM half: build the strip on `onShow`,
8
+ // tear it down on `onHide`, run the enter/exit transitions.
13
9
  //
14
10
  // Adapted from Adwaita Web UI Framework (https://github.com/mclellac/adwaita-web).
15
11
  // Reference: refs/adwaita-web/adwaita-web/scss/_toast.scss, _toast_overlay.scss
@@ -41,7 +37,11 @@ export interface AdwToastOptions {
41
37
  /** Default toast lifetime in SECONDS — this element's public unit (Adw.Toast:timeout). */
42
38
  const DEFAULT_TIMEOUT_SECONDS = 5;
43
39
 
44
- /** Exit-transition budget in ms; must stay in sync with `.adw-toast.hiding` in `_toast.scss`. */
40
+ /**
41
+ * Fallback budget in ms for the exit transition: must stay at or above the
42
+ * `.adw-toast.hiding` transition-duration in `_toast.scss` (0.15s today), because it
43
+ * only fires when `transitionend` does not.
44
+ */
45
45
  const TOAST_EXIT_MS = 300;
46
46
 
47
47
  /** The default timing seam: the browser's own timers. */
@@ -73,9 +73,9 @@ export class AdwToastOverlay extends HTMLElement {
73
73
  },
74
74
  onShow: (toast) => this._mountToast(toast),
75
75
  // The core queue fires `onHide` BEFORE it shifts the next toast in, so
76
- // `pending > 0` here means another toast takes this slot immediately:
77
- // detach without the exit transition so exactly one strip is ever in
78
- // the DOM. A last toast fades out instead.
76
+ // `pending > 0` here means another toast takes this slot immediately: detach
77
+ // without the exit transition, keeping exactly one strip in the DOM. A last
78
+ // toast fades out instead.
79
79
  onHide: () => this._unmountToast(this._queue.pending > 0),
80
80
  });
81
81
  }
@@ -84,8 +84,8 @@ export class AdwToastOverlay extends HTMLElement {
84
84
  if (this._initialized) return;
85
85
  this._initialized = true;
86
86
 
87
- // Adopt the declared children as the wrapped content (the Adw.ToastOverlay
88
- // :child) and layer a dedicated toast container over the bottom edge.
87
+ // The declared children become the wrapped content (Adw.ToastOverlay:child);
88
+ // the toast container is layered over the bottom edge.
89
89
  const content = document.createElement('div');
90
90
  content.className = 'adw-toast-overlay-content';
91
91
  for (const child of Array.from(this.childNodes)) content.appendChild(child);
@@ -121,16 +121,12 @@ export class AdwToastOverlay extends HTMLElement {
121
121
  }
122
122
 
123
123
  /**
124
- * Show a toast notification — the web equivalent of Adw.ToastOverlay.add_toast()
125
- * with a freshly-built Adw.Toast.
124
+ * Show a toast — the web equivalent of Adw.ToastOverlay.add_toast() with a
125
+ * freshly-built Adw.Toast. Only ONE toast is visible at a time; this one shows
126
+ * immediately when the slot is free, otherwise it waits its turn.
126
127
  *
127
- * Like Adw.ToastOverlay, only ONE toast is visible at a time: this one shows
128
- * immediately when the slot is free, otherwise it waits its turn behind the
129
- * toasts already queued.
130
- *
131
- * @param title The text to display.
132
- * @param options Either a timeout in milliseconds (legacy shorthand) or an
133
- * {@link AdwToastOptions} bag (title text already supplied here).
128
+ * `options` is either an {@link AdwToastOptions} bag or, as a legacy shorthand, a
129
+ * timeout in MILLISECONDS.
134
130
  */
135
131
  addToast(title: string, options: number | AdwToastOptions = {}): void {
136
132
  // Ensure the toast container exists even if addToast() races a not-yet
@@ -187,8 +183,7 @@ export class AdwToastOverlay extends HTMLElement {
187
183
  el.appendChild(button);
188
184
  }
189
185
 
190
- // Close affordance — a CSS-drawn "×" (no symbolic close icon ships in the
191
- // curated @gjsify/adwaita-icons set), mirroring adw-tab-view's .adw-tab-close.
186
+ // Close affordance: a CSS-drawn "×", as in adw-tab-view's close button.
192
187
  const close = document.createElement('button');
193
188
  close.type = 'button';
194
189
  close.className = 'adw-toast-close-button';
@@ -206,10 +201,9 @@ export class AdwToastOverlay extends HTMLElement {
206
201
  }
207
202
 
208
203
  /**
209
- * Detach the mounted strip — the DOM half of the queue's `onHide`.
210
- *
211
- * @param immediate Skip the exit transition because the next queued toast is
212
- * about to take this slot (keeps exactly one strip mounted).
204
+ * Detach the mounted strip — the DOM half of the queue's `onHide`. `immediate`
205
+ * skips the exit transition because the next queued toast is about to take this
206
+ * slot, which is what keeps exactly one strip mounted.
213
207
  */
214
208
  private _unmountToast(immediate: boolean): void {
215
209
  const el = this._currentEl;
@@ -224,9 +218,8 @@ export class AdwToastOverlay extends HTMLElement {
224
218
  el.classList.remove('visible');
225
219
  el.classList.add('hiding');
226
220
  el.addEventListener('transitionend', () => el.remove(), { once: true });
227
- // Fallback if transitionend doesn't fire (e.g. reduced motion). Routed
228
- // through the injected scheduler so the whole lifecycle is deterministic
229
- // when a fake clock is installed.
221
+ // Fallback for when transitionend does not fire (reduced motion). Routed through
222
+ // the injected scheduler so the lifecycle stays deterministic under a fake clock.
230
223
  this._scheduler.schedule(() => {
231
224
  if (el.parentNode) el.remove();
232
225
  }, TOAST_EXIT_MS);
@@ -1,20 +1,13 @@
1
- // <adw-toggle-group> — A linked set of toggle buttons where exactly one is
2
- // active at a time (the web counterpart of Adw.ToggleGroup). Toggles are
3
- // declared as <adw-toggle> children with `label` and/or `icon-name` attributes;
4
- // the group renders them inside an inner container, tracks the active index, and
5
- // recolors the active toggle with the Adwaita active-toggle accent.
6
- // Attributes: active (zero-based index of the active toggle, default 0), and the
7
- // style flags flat / round (mirroring the Adw.ToggleGroup `.flat` / `.round`
8
- // style classes).
9
- // Events: `notify::active` (CustomEvent, bubbles, `detail = { active }`) when the
10
- // active toggle changes — mirrors the Adw.ToggleGroup `active` GObject property.
1
+ // <adw-toggle-group> — the web counterpart of Adw.ToggleGroup: a linked set of toggle
2
+ // buttons where exactly one is active. Toggles are `<adw-toggle>` children carrying
3
+ // `label` and/or `icon-name`, and the `flat` / `round` attributes mirror the upstream
4
+ // `.flat` / `.round` style classes. `notify::active` (CustomEvent, bubbles, detail
5
+ // `{ active }`) mirrors the `active` GObject property.
11
6
  //
12
- // The SELECTION state machine (the segment list plus the guarded, no-op-on-same
13
- // active index) is HEADLESS and lives in `@gjsify/adwaita-core` (ADR 0004) as
14
- // {@link ToggleGroupState}; this element composes it and keeps only the DOM render
15
- // half — building the buttons, the active pill / `aria-pressed` reflection, and
16
- // the `notify::active` event. `@gjsify/adwaita-nativescript` composes the same
17
- // state machine, so both ports share one behaviour.
7
+ // The SELECTION state machine (the segment list plus the guarded, no-op-on-same active
8
+ // index) is HEADLESS and lives in `@gjsify/adwaita-core` (ADR 0004) as
9
+ // {@link ToggleGroupState}; this element keeps only the DOM half — the buttons, the active
10
+ // pill / `aria-pressed` reflection and the event.
18
11
  //
19
12
  // Reference: refs/libadwaita/src/adw-toggle-group.c (AdwToggleGroup behaviour)
20
13
  // Reference: refs/libadwaita/src/stylesheet/widgets/_toggle-group.scss