@gjsify/adwaita-web 0.31.0 → 0.33.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 (183) hide show
  1. package/dist/adwaita-web.css +835 -299
  2. package/dist/adwaita-web.css.map +1 -1
  3. package/lib/types/adw-about-dialog.spec.d.ts +1 -0
  4. package/lib/types/adw-action-rows.spec.d.ts +1 -0
  5. package/lib/types/adw-avatar.spec.d.ts +1 -0
  6. package/lib/types/adw-banner.spec.d.ts +1 -0
  7. package/lib/types/adw-button-content.spec.d.ts +1 -0
  8. package/lib/types/adw-carousel.spec.d.ts +1 -0
  9. package/lib/types/adw-checks.spec.d.ts +1 -0
  10. package/lib/types/adw-header-bar.spec.d.ts +1 -0
  11. package/lib/types/adw-icon.spec.d.ts +1 -0
  12. package/lib/types/adw-progress-bar.spec.d.ts +1 -0
  13. package/lib/types/adw-sidebar.spec.d.ts +1 -0
  14. package/lib/types/adw-switch.spec.d.ts +1 -0
  15. package/lib/types/adw-wrap-box.spec.d.ts +1 -0
  16. package/lib/types/bottom-sheet.spec.d.ts +1 -0
  17. package/lib/types/breakpoints.d.ts +22 -0
  18. package/lib/types/breakpoints.spec.d.ts +1 -0
  19. package/lib/types/chrome.spec.d.ts +1 -0
  20. package/lib/types/elements/adw-about-dialog.d.ts +47 -8
  21. package/lib/types/elements/adw-action-row.d.ts +35 -0
  22. package/lib/types/elements/adw-avatar.d.ts +11 -0
  23. package/lib/types/elements/adw-bottom-sheet.d.ts +33 -4
  24. package/lib/types/elements/adw-button-content.d.ts +12 -0
  25. package/lib/types/elements/adw-button-row.d.ts +10 -1
  26. package/lib/types/elements/adw-carousel.d.ts +111 -8
  27. package/lib/types/elements/adw-checks.d.ts +57 -0
  28. package/lib/types/elements/adw-clamp.d.ts +4 -1
  29. package/lib/types/elements/adw-combo-row.d.ts +10 -0
  30. package/lib/types/elements/adw-data-grid.d.ts +11 -31
  31. package/lib/types/elements/adw-drop-down.d.ts +17 -20
  32. package/lib/types/elements/adw-entry-row.d.ts +93 -3
  33. package/lib/types/elements/adw-expander-row.d.ts +7 -1
  34. package/lib/types/elements/adw-header-bar.d.ts +15 -0
  35. package/lib/types/elements/adw-icon.d.ts +25 -0
  36. package/lib/types/elements/adw-inline-view-switcher.d.ts +73 -11
  37. package/lib/types/elements/adw-menu-button.d.ts +6 -6
  38. package/lib/types/elements/adw-navigation-split-view.d.ts +48 -0
  39. package/lib/types/elements/adw-navigation-view.d.ts +63 -20
  40. package/lib/types/elements/adw-overlay-split-view.d.ts +94 -0
  41. package/lib/types/elements/adw-password-entry-row.d.ts +19 -10
  42. package/lib/types/elements/adw-popover.d.ts +58 -0
  43. package/lib/types/elements/adw-preferences-dialog.d.ts +57 -2
  44. package/lib/types/elements/adw-preferences-group.d.ts +31 -0
  45. package/lib/types/elements/adw-progress-bar.d.ts +38 -0
  46. package/lib/types/elements/adw-sidebar.d.ts +37 -5
  47. package/lib/types/elements/adw-spinner.d.ts +23 -0
  48. package/lib/types/elements/adw-split-button.d.ts +53 -7
  49. package/lib/types/elements/adw-switch-row.d.ts +11 -2
  50. package/lib/types/elements/adw-switch.d.ts +15 -0
  51. package/lib/types/elements/adw-tab-view.d.ts +129 -12
  52. package/lib/types/elements/adw-toolbar-view.d.ts +5 -0
  53. package/lib/types/elements/adw-view-stack.d.ts +39 -19
  54. package/lib/types/elements/adw-view-switcher-bar.d.ts +25 -7
  55. package/lib/types/elements/adw-view-switcher.d.ts +76 -13
  56. package/lib/types/elements/adw-window-title.d.ts +3 -1
  57. package/lib/types/elements/adw-wrap-box.d.ts +58 -3
  58. package/lib/types/elements/view-switcher-dom.d.ts +44 -0
  59. package/lib/types/entry-rows.spec.d.ts +1 -0
  60. package/lib/types/index.d.ts +7 -0
  61. package/lib/types/navigation-view.spec.d.ts +1 -0
  62. package/lib/types/preferences-search.d.ts +20 -0
  63. package/lib/types/preferences.spec.d.ts +1 -0
  64. package/lib/types/split-button.spec.d.ts +1 -0
  65. package/lib/types/split-views.spec.d.ts +1 -0
  66. package/lib/types/styles.generated.d.ts +1 -1
  67. package/lib/types/tab-view.spec.d.ts +1 -0
  68. package/lib/types/view-stack.spec.d.ts +1 -0
  69. package/lib/types/view-switcher.spec.d.ts +1 -0
  70. package/package.json +6 -6
  71. package/scss/_banner.scss +13 -0
  72. package/scss/_bottom_sheet.scss +5 -6
  73. package/scss/_button.scss +3 -26
  74. package/scss/_button_content.scss +28 -0
  75. package/scss/_checks.scss +228 -0
  76. package/scss/_clamp.scss +9 -3
  77. package/scss/_combo_row.scss +19 -0
  78. package/scss/_data_grid.scss +15 -7
  79. package/scss/_drop_down.scss +12 -55
  80. package/scss/_expander_row.scss +5 -50
  81. package/scss/_icon.scss +51 -0
  82. package/scss/_icons.generated.scss +6 -0
  83. package/scss/_inline_view_switcher.scss +34 -0
  84. package/scss/_labels.scss +135 -0
  85. package/scss/_menu_button.scss +10 -70
  86. package/scss/_navigation_split_view.scss +47 -8
  87. package/scss/_overlay_split_view.scss +117 -32
  88. package/scss/_popover.scss +186 -0
  89. package/scss/_preferences.scss +29 -0
  90. package/scss/_preferences_dialog.scss +17 -0
  91. package/scss/_progress_bar.scss +161 -0
  92. package/scss/_reset.scss +18 -7
  93. package/scss/_spinner.scss +37 -19
  94. package/scss/_split_button.scss +6 -50
  95. package/scss/_switch.scss +90 -0
  96. package/scss/_switch_row.scss +6 -52
  97. package/scss/_tab_view.scss +63 -0
  98. package/scss/_theme.scss +10 -0
  99. package/scss/_toolbar_view.scss +74 -0
  100. package/scss/_variables.scss +34 -0
  101. package/scss/_view_switcher.scss +40 -0
  102. package/scss/_view_switcher_bar.scss +37 -0
  103. package/scss/_wrap_box.scss +65 -6
  104. package/scss/adwaita-skin.scss +23 -0
  105. package/src/adw-about-dialog.spec.ts +350 -0
  106. package/src/adw-action-rows.spec.ts +342 -0
  107. package/src/adw-avatar.spec.ts +176 -0
  108. package/src/adw-banner.spec.ts +152 -0
  109. package/src/adw-button-content.spec.ts +207 -0
  110. package/src/adw-button-row.spec.ts +12 -6
  111. package/src/adw-carousel.spec.ts +389 -0
  112. package/src/adw-checks.spec.ts +368 -0
  113. package/src/adw-drop-down.spec.ts +123 -0
  114. package/src/adw-header-bar.spec.ts +83 -0
  115. package/src/adw-icon.spec.ts +167 -0
  116. package/src/adw-progress-bar.spec.ts +173 -0
  117. package/src/adw-row-state.spec.ts +49 -0
  118. package/src/adw-sidebar.spec.ts +332 -0
  119. package/src/adw-switch.spec.ts +242 -0
  120. package/src/adw-tab-view.spec.ts +12 -3
  121. package/src/adw-view-switcher-bar.spec.ts +61 -13
  122. package/src/adw-wrap-box.spec.ts +341 -0
  123. package/src/bottom-sheet.spec.ts +325 -0
  124. package/src/breakpoints.spec.ts +147 -0
  125. package/src/breakpoints.ts +67 -0
  126. package/src/chrome.spec.ts +400 -0
  127. package/src/elements/adw-about-dialog.ts +223 -163
  128. package/src/elements/adw-action-row.ts +131 -11
  129. package/src/elements/adw-avatar.ts +48 -54
  130. package/src/elements/adw-banner.ts +46 -10
  131. package/src/elements/adw-bottom-sheet.ts +145 -60
  132. package/src/elements/adw-button-content.ts +107 -33
  133. package/src/elements/adw-button-row.ts +61 -37
  134. package/src/elements/adw-button.ts +5 -7
  135. package/src/elements/adw-carousel.ts +467 -105
  136. package/src/elements/adw-checks.ts +299 -0
  137. package/src/elements/adw-clamp.ts +80 -10
  138. package/src/elements/adw-combo-row.ts +32 -7
  139. package/src/elements/adw-data-grid.ts +43 -91
  140. package/src/elements/adw-drop-down.ts +117 -128
  141. package/src/elements/adw-entry-row.ts +404 -36
  142. package/src/elements/adw-expander-row.ts +61 -29
  143. package/src/elements/adw-header-bar.ts +31 -6
  144. package/src/elements/adw-icon.ts +148 -0
  145. package/src/elements/adw-inline-view-switcher.ts +325 -131
  146. package/src/elements/adw-menu-button.ts +93 -70
  147. package/src/elements/adw-navigation-split-view.ts +220 -14
  148. package/src/elements/adw-navigation-view.ts +286 -148
  149. package/src/elements/adw-overlay-split-view.ts +536 -28
  150. package/src/elements/adw-password-entry-row.ts +111 -89
  151. package/src/elements/adw-popover.ts +283 -0
  152. package/src/elements/adw-preferences-dialog.ts +120 -21
  153. package/src/elements/adw-preferences-group.ts +128 -22
  154. package/src/elements/adw-progress-bar.ts +189 -0
  155. package/src/elements/adw-sidebar.ts +301 -142
  156. package/src/elements/adw-spin-row.ts +11 -5
  157. package/src/elements/adw-spinner.ts +168 -9
  158. package/src/elements/adw-split-button.ts +371 -92
  159. package/src/elements/adw-status-page.ts +9 -9
  160. package/src/elements/adw-switch-row.ts +100 -30
  161. package/src/elements/adw-switch.ts +129 -0
  162. package/src/elements/adw-tab-view.ts +700 -129
  163. package/src/elements/adw-toggle-group.ts +5 -7
  164. package/src/elements/adw-toolbar-view.ts +84 -21
  165. package/src/elements/adw-view-stack.ts +183 -85
  166. package/src/elements/adw-view-switcher-bar.ts +171 -81
  167. package/src/elements/adw-view-switcher.ts +328 -117
  168. package/src/elements/adw-window-title.ts +44 -11
  169. package/src/elements/adw-wrap-box.ts +329 -78
  170. package/src/elements/view-switcher-dom.ts +108 -0
  171. package/src/entry-rows.spec.ts +391 -0
  172. package/src/index.ts +23 -0
  173. package/src/navigation-view.spec.ts +435 -0
  174. package/src/preferences-search.ts +135 -0
  175. package/src/preferences.spec.ts +250 -0
  176. package/src/source-view/adw-source-view.ts +2 -5
  177. package/src/split-button.spec.ts +470 -0
  178. package/src/split-views.spec.ts +893 -0
  179. package/src/styles.generated.ts +1 -1
  180. package/src/tab-view.spec.ts +451 -0
  181. package/src/test.browser.mts +49 -0
  182. package/src/view-stack.spec.ts +272 -0
  183. package/src/view-switcher.spec.ts +665 -0
@@ -8,6 +8,16 @@ export class AdwHeaderBar extends HTMLElement {
8
8
  private _startEl: HTMLDivElement | null = null;
9
9
  private _centerEl: HTMLDivElement | null = null;
10
10
  private _endEl: HTMLDivElement | null = null;
11
+ /**
12
+ * The generated title span, or `null` when a `slot="center"` title-widget
13
+ * took the centre. `Adw.HeaderBar` has the same either/or: setting
14
+ * `title-widget` replaces the derived `AdwWindowTitle` outright.
15
+ */
16
+ private _titleEl: HTMLSpanElement | null = null;
17
+
18
+ static get observedAttributes() {
19
+ return ['title'];
20
+ }
11
21
 
12
22
  /** The start (left) section container — append buttons/widgets here. */
13
23
  get startSection(): HTMLDivElement | null {
@@ -29,8 +39,6 @@ export class AdwHeaderBar extends HTMLElement {
29
39
  if (this._initialized) return;
30
40
  this._initialized = true;
31
41
 
32
- const title = this.getAttribute('title') || '';
33
-
34
42
  // Capture any pre-existing slotted children before clearing
35
43
  const startChildren = Array.from(this.querySelectorAll(':scope > [slot="start"]'));
36
44
  const centerChildren = Array.from(this.querySelectorAll(':scope > [slot="center"]'));
@@ -48,10 +56,9 @@ export class AdwHeaderBar extends HTMLElement {
48
56
  if (centerChildren.length > 0) {
49
57
  for (const child of centerChildren) this._centerEl.appendChild(child);
50
58
  } else {
51
- const titleEl = document.createElement('span');
52
- titleEl.className = 'adw-header-bar-title';
53
- titleEl.textContent = title;
54
- this._centerEl.appendChild(titleEl);
59
+ this._titleEl = document.createElement('span');
60
+ this._titleEl.className = 'adw-header-bar-title';
61
+ this._centerEl.appendChild(this._titleEl);
55
62
  }
56
63
 
57
64
  // End section
@@ -61,6 +68,24 @@ export class AdwHeaderBar extends HTMLElement {
61
68
 
62
69
  // Replace all children atomically
63
70
  this.replaceChildren(this._startEl, this._centerEl, this._endEl);
71
+ this._renderTitle();
72
+ }
73
+
74
+ /**
75
+ * `title` used to be read ONCE, in `connectedCallback`, so every later write
76
+ * was a silent no-op — a header bar whose title tracked the open document
77
+ * kept whatever it was created with. `Adw.HeaderBar`'s derived title widget
78
+ * is bound to the property and re-renders on every change.
79
+ */
80
+ attributeChangedCallback(name: string) {
81
+ if (this._initialized && name === 'title') this._renderTitle();
82
+ }
83
+
84
+ private _renderTitle() {
85
+ // A `slot="center"` widget replaced the derived title, so there is
86
+ // nothing for the attribute to write to — the same either/or as
87
+ // `Adw.HeaderBar:title-widget`.
88
+ if (this._titleEl) this._titleEl.textContent = this.getAttribute('title') ?? '';
64
89
  }
65
90
  }
66
91
 
@@ -0,0 +1,148 @@
1
+ // <adw-icon> — the symbolic icon node, as an element instead of a shape you
2
+ // retype. Every icon in this package is a CSS-masked box whose glyph comes from
3
+ // a generated `.adw-icon--<name>` class and whose colour is `currentColor`, so
4
+ // it themes with whatever contains it.
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.
16
+ //
17
+ // Attributes:
18
+ // icon-name — symbolic name, with or without the `-symbolic` suffix. A name
19
+ // that is not a single CSS token (a space, a quote, a
20
+ // reverse-DNS application id) resolves to NO icon rather than to
21
+ // injected markup — see `normalizeIconName`.
22
+ // size — the rendered edge length in px. Absent leaves the stylesheet's
23
+ // 16px in charge; a context that sizes its own icons through CSS
24
+ // (`.adw-about-dialog-icon`, the split button's 14px arrows) keeps
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).
30
+ //
31
+ // THE ELEMENT *IS* THE ICON BOX — it carries `.adw-icon` itself rather than
32
+ // wrapping a span. A wrapper would have inserted a layout node into flex rows,
33
+ // button interiors and absolutely-positioned arrows that were all measured
34
+ // against the icon box directly; carrying the class keeps every existing
35
+ // class-scoped rule (`.adw-drop-down-arrow`, `.adw-tab-icon`, `.adw-row-edit`, …)
36
+ // matching the same node it always matched.
37
+ //
38
+ // Reference: refs/libadwaita/src/stylesheet/_common.scss:34-41 — `.normal-icons`
39
+ // / `.large-icons` set `-gtk-icon-size`; libadwaita has NO `_icon.scss`, the
40
+ // icon is a GtkImage and its size is a GTK property, not a stylesheet rule.
41
+ // Reference: refs/adwaita-web/adwaita-web/scss/_icon.scss — the vendored web
42
+ // port's `.adw-icon` (an inline-flex SVG holder; this package masks instead).
43
+ // Copyright (c) GNOME contributors (libadwaita). LGPLv2.1+.
44
+ // Copyright (c) 2025 csm (adwaita-web). MIT License.
45
+ // Modifications: Implemented as a Web Component for @gjsify/adwaita-web; the
46
+ // name derivation composed from @gjsify/adwaita-core.
47
+
48
+ import { normalizeIconName } from '@gjsify/adwaita-core';
49
+
50
+ /** The generated mask-class prefix — the one place the `--` spelling appears. */
51
+ const MASK_CLASS_PREFIX = 'adw-icon--';
52
+
53
+ export class AdwIcon extends HTMLElement {
54
+ static get observedAttributes() {
55
+ return ['icon-name', 'size'];
56
+ }
57
+
58
+ /** The declared symbolic name, `-symbolic` suffix optional. */
59
+ get iconName(): string {
60
+ return this.getAttribute('icon-name') ?? '';
61
+ }
62
+
63
+ set iconName(value: string | null) {
64
+ if (value === null) this.removeAttribute('icon-name');
65
+ else this.setAttribute('icon-name', value);
66
+ }
67
+
68
+ /**
69
+ * The name that actually reached the mask class — `''` when the icon-name is
70
+ * absent, empty, or not usable as one CSS token. Consumers that need to know
71
+ * whether an icon is being drawn ask this, not the raw attribute.
72
+ */
73
+ get resolvedIconName(): string {
74
+ return normalizeIconName(this.getAttribute('icon-name'));
75
+ }
76
+
77
+ /** The rendered edge length in px, or `null` when the stylesheet decides. */
78
+ get size(): number | null {
79
+ const declared = Number.parseFloat(this.getAttribute('size') ?? '');
80
+ return Number.isFinite(declared) && declared > 0 ? declared : null;
81
+ }
82
+
83
+ set size(value: number | null) {
84
+ if (value === null) this.removeAttribute('size');
85
+ else this.setAttribute('size', String(value));
86
+ }
87
+
88
+ connectedCallback() {
89
+ this._render();
90
+ }
91
+
92
+ attributeChangedCallback() {
93
+ this._render();
94
+ }
95
+
96
+ // ── internals ──────────────────────────────────────────────────────────
97
+
98
+ private _render(): void {
99
+ this.classList.add('adw-icon');
100
+
101
+ // Swap ONLY the mask class: the callers put their own positioning class
102
+ // on the same node (`adw-drop-down-arrow`, `adw-sidebar-item-arrow`, …),
103
+ // and a `className =` assignment here would take those with it. The list
104
+ // is collected BEFORE removing — `classList` is live, so removing while
105
+ // iterating it would skip the entry after each hit.
106
+ const stale = [...this.classList].filter((existing) => existing.startsWith(MASK_CLASS_PREFIX));
107
+ this.classList.remove(...stale);
108
+ const name = this.resolvedIconName;
109
+ if (name !== '') this.classList.add(`${MASK_CLASS_PREFIX}${name}`);
110
+
111
+ // A masked box has no text, so assistive tech has nothing to announce —
112
+ // the accessible name belongs to the control that HOSTS the icon. Every
113
+ // hand-rolled copy that thought about it said the same thing; the ones
114
+ // that did not were the inconsistency, not the intent.
115
+ this.setAttribute('aria-hidden', 'true');
116
+
117
+ const size = this.size;
118
+ if (size === null) {
119
+ this.style.removeProperty('width');
120
+ this.style.removeProperty('height');
121
+ this.style.removeProperty('mask-size');
122
+ this.style.removeProperty('-webkit-mask-size');
123
+ } else {
124
+ const px = `${size}px`;
125
+ this.style.width = px;
126
+ this.style.height = px;
127
+ // The box and the mask scale together — sizing only the box leaves a
128
+ // 16px glyph floating in the middle of it.
129
+ this.style.setProperty('mask-size', px);
130
+ this.style.setProperty('-webkit-mask-size', px);
131
+ }
132
+ }
133
+ }
134
+
135
+ /**
136
+ * Build one icon node: the three lines every caller would otherwise write.
137
+ *
138
+ * `extraClasses` are the caller's own positioning/context classes, added
139
+ * alongside the managed `.adw-icon` + mask class rather than replacing them.
140
+ */
141
+ export function createAdwIcon(iconName: string | null, ...extraClasses: string[]): AdwIcon {
142
+ const icon = document.createElement('adw-icon') as AdwIcon;
143
+ if (iconName !== null) icon.setAttribute('icon-name', iconName);
144
+ if (extraClasses.length > 0) icon.classList.add(...extraClasses);
145
+ return icon;
146
+ }
147
+
148
+ customElements.define('adw-icon', AdwIcon);