@gjsify/adwaita-web 0.40.0 → 0.42.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +98 -3
- package/dist/adwaita-web.css +142 -63
- package/dist/adwaita-web.css.map +1 -1
- package/lib/types/adw-fonts.spec.d.ts +2 -0
- package/lib/types/breakpoints.d.ts +10 -1
- package/lib/types/connect-lifecycle.spec.d.ts +1 -0
- package/lib/types/elements/adw-about-dialog.d.ts +1 -0
- package/lib/types/elements/adw-action-row.d.ts +2 -0
- package/lib/types/elements/adw-alert-dialog.d.ts +14 -2
- package/lib/types/elements/adw-carousel.d.ts +48 -0
- package/lib/types/elements/adw-dialog.d.ts +2 -7
- package/lib/types/elements/adw-icon.d.ts +17 -1
- package/lib/types/elements/adw-navigation-split-view.d.ts +7 -0
- package/lib/types/elements/adw-overlay-split-view.d.ts +6 -0
- package/lib/types/elements/adw-popover.d.ts +1 -0
- package/lib/types/elements/adw-preferences-dialog.d.ts +1 -0
- package/lib/types/elements/adw-switch.d.ts +10 -0
- package/lib/types/elements/adw-tab-view.d.ts +23 -0
- package/lib/types/elements/adw-toggle-group.d.ts +2 -0
- package/lib/types/elements/adw-toolbar-view.d.ts +8 -0
- package/lib/types/elements/modal-surface.d.ts +59 -0
- package/lib/types/elements/roving-focus.d.ts +26 -0
- package/lib/types/elements/row-activation.d.ts +26 -0
- package/lib/types/empty-sections.d.ts +15 -0
- package/lib/types/empty-sections.spec.d.ts +1 -0
- package/lib/types/fonts.d.ts +1 -0
- package/lib/types/icon-registry.d.ts +35 -0
- package/lib/types/icon-registry.spec.d.ts +18 -0
- package/lib/types/index.d.ts +3 -1
- package/lib/types/keyboard-operable.spec.d.ts +1 -0
- package/lib/types/scroll-shading.d.ts +56 -0
- package/lib/types/styles.generated.d.ts +1 -1
- package/package.json +10 -6
- package/scss/_banner.scss +1 -1
- package/scss/_carousel.scss +15 -1
- package/scss/_checks.scss +8 -8
- package/scss/_combo_row.scss +1 -1
- package/scss/_drop_down.scss +1 -1
- package/scss/_expander_row.scss +1 -1
- package/scss/_headerbar.scss +35 -6
- package/scss/_icon.scss +47 -0
- package/scss/_icons.generated.scss +36 -6
- package/scss/_password_entry_row.scss +1 -1
- package/scss/_preferences_dialog.scss +3 -2
- package/scss/_progress_bar.scss +1 -1
- package/scss/_row.scss +1 -1
- package/scss/_scrolling.scss +107 -0
- package/scss/_status_page.scss +1 -1
- package/scss/_style_classes.scss +21 -8
- package/scss/_tab_view.scss +1 -1
- package/scss/_theme.scss +1 -0
- package/scss/_toast.scss +6 -24
- package/scss/_toolbar_view.scss +52 -22
- package/scss/_variables.scss +30 -3
- package/scss/_view_switcher.scss +80 -20
- package/scss/adwaita-skin.scss +1 -0
- package/src/adw-action-rows.spec.ts +21 -9
- package/src/adw-button-content.spec.ts +11 -6
- package/src/adw-carousel.spec.ts +141 -1
- package/src/adw-fonts.spec.ts +188 -0
- package/src/adw-header-bar.spec.ts +92 -0
- package/src/adw-icon.spec.ts +17 -8
- package/src/adw-row-state.spec.ts +11 -1
- package/src/adw-tab-view.spec.ts +151 -0
- package/src/bottom-sheet.spec.ts +87 -0
- package/src/breakpoints.ts +18 -4
- package/src/chrome.spec.ts +233 -0
- package/src/connect-lifecycle.spec.ts +351 -0
- package/src/elements/adw-about-dialog.ts +24 -18
- package/src/elements/adw-action-row.ts +14 -2
- package/src/elements/adw-alert-dialog.ts +39 -20
- package/src/elements/adw-button-row.ts +3 -0
- package/src/elements/adw-carousel.ts +121 -4
- package/src/elements/adw-clamp.ts +3 -2
- package/src/elements/adw-data-grid.ts +5 -2
- package/src/elements/adw-dialog.ts +16 -63
- package/src/elements/adw-expander-row.ts +18 -4
- package/src/elements/adw-header-bar.ts +2 -0
- package/src/elements/adw-icon.ts +32 -6
- package/src/elements/adw-inline-view-switcher.ts +12 -0
- package/src/elements/adw-menu-button.ts +17 -10
- package/src/elements/adw-navigation-split-view.ts +30 -7
- package/src/elements/adw-overlay-split-view.ts +15 -3
- package/src/elements/adw-popover.ts +11 -0
- package/src/elements/adw-preferences-dialog.ts +18 -16
- package/src/elements/adw-preferences-group.ts +7 -0
- package/src/elements/adw-sidebar.ts +15 -0
- package/src/elements/adw-status-page.ts +12 -4
- package/src/elements/adw-switch-row.ts +7 -0
- package/src/elements/adw-switch.ts +19 -1
- package/src/elements/adw-tab-view.ts +70 -7
- package/src/elements/adw-toast-overlay.ts +8 -1
- package/src/elements/adw-toggle-group.ts +94 -2
- package/src/elements/adw-toolbar-view.ts +28 -2
- package/src/elements/adw-view-switcher-bar.ts +18 -0
- package/src/elements/adw-view-switcher.ts +14 -0
- package/src/elements/modal-surface.ts +180 -0
- package/src/elements/roving-focus.ts +154 -0
- package/src/elements/row-activation.ts +92 -0
- package/src/empty-sections.spec.ts +81 -0
- package/src/empty-sections.ts +54 -0
- package/src/fonts.ts +25 -0
- package/src/icon-registry.spec.ts +240 -0
- package/src/icon-registry.ts +157 -0
- package/src/index.ts +30 -1
- package/src/keyboard-operable.spec.ts +755 -0
- package/src/scroll-shading.ts +255 -0
- package/src/source-view/theme.ts +15 -3
- package/src/split-button.spec.ts +6 -2
- package/src/style-classes.spec.ts +6 -0
- package/src/styles.generated.ts +1 -1
- package/src/test.browser.mts +14 -0
- package/src/view-switcher.spec.ts +149 -0
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
// Browser wiring for GtkScrolledWindow's edge indicators — the DOM half of
|
|
2
|
+
// `@gjsify/adwaita-core`'s `scrolling.ts`, which owns the arithmetic.
|
|
3
|
+
//
|
|
4
|
+
// WHY a discovery pass and not a widget. GTK has a `scrolledwindow` node, so the
|
|
5
|
+
// stylesheet can say `toolbarview.undershoot-top scrolledwindow` and be done. The
|
|
6
|
+
// browser has no such element — any block with `overflow: auto` is one — so the
|
|
7
|
+
// equivalent scope has to be computed. The scope is deliberately the same as
|
|
8
|
+
// upstream's: a DESCENDANT selector, so a toolbar view shades EVERY scroller under
|
|
9
|
+
// it, not only the one it directly wraps. That is what makes the storybook's three
|
|
10
|
+
// panes (story list, preview, controls) each shade themselves independently.
|
|
11
|
+
//
|
|
12
|
+
// WHY the top and bottom indicators share one pseudo-element each. Undershoot and
|
|
13
|
+
// overshoot at the same edge are mutually exclusive by construction: an undershoot
|
|
14
|
+
// says content is hidden past the edge, an overshoot says the position is past it,
|
|
15
|
+
// and `overshootDistance` is 0 for as long as `isScrolledFromStart` is true. One
|
|
16
|
+
// box per edge, switched by which state is live.
|
|
17
|
+
//
|
|
18
|
+
// WHY sticky. The indicator has to stay welded to the scrollport edge while the
|
|
19
|
+
// content moves under it, and it must consume no layout. `position: sticky` plus a
|
|
20
|
+
// negative margin equal to its height is the only combination that does both — an
|
|
21
|
+
// absolutely-positioned child of a scroll container scrolls away with the content.
|
|
22
|
+
// The one constraint this leaves: a scroller that is itself a flex/grid container
|
|
23
|
+
// with a `gap` pays that gap once for the pseudo-element. Scrollers are wrappers,
|
|
24
|
+
// so this has not bitten; a scroller with a gap should move it to an inner box.
|
|
25
|
+
//
|
|
26
|
+
// Reference: refs/gtk/gtk/gtkscrolledwindow.c
|
|
27
|
+
// Reference: refs/libadwaita/src/stylesheet/widgets/_scrolling.scss
|
|
28
|
+
// Copyright (c) GNOME contributors (GTK, libadwaita). LGPLv2.1+.
|
|
29
|
+
|
|
30
|
+
import {
|
|
31
|
+
ADW_OVERSHOOT_SETTLE_MS,
|
|
32
|
+
ADW_UNDERSHOOT_CLASSES,
|
|
33
|
+
accumulateOvershoot,
|
|
34
|
+
overshootDistance,
|
|
35
|
+
type ScrollAdjustment,
|
|
36
|
+
scrollUndershootClasses,
|
|
37
|
+
} from '@gjsify/adwaita-core';
|
|
38
|
+
|
|
39
|
+
/** Marks a scroller adwaita-web has taken over, so the stylesheet can target it. */
|
|
40
|
+
const SHADED_CLASS = 'adw-scroll-shaded';
|
|
41
|
+
|
|
42
|
+
/** The custom property carrying the live overshoot size, in px. */
|
|
43
|
+
const OVERSHOOT_PROPERTY = '--adw-overshoot';
|
|
44
|
+
|
|
45
|
+
/** Which edge is currently overshooting, as a state class. */
|
|
46
|
+
const OVERSHOOT_CLASSES = ['overshoot-top', 'overshoot-bottom'] as const;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* `overflow` values that make a box a scrollport.
|
|
50
|
+
*
|
|
51
|
+
* `overlay` is a WebKit alias for `auto` that a browser may still report from
|
|
52
|
+
* author CSS even though it is removed from the spec, so a scroller written that
|
|
53
|
+
* way is one and has to be treated as one.
|
|
54
|
+
*/
|
|
55
|
+
const SCROLLABLE_OVERFLOW = new Set(['auto', 'scroll', 'overlay']);
|
|
56
|
+
|
|
57
|
+
/** Per-axis adjustments as the DOM reports them. */
|
|
58
|
+
function adjustmentsOf(el: HTMLElement): { vertical?: ScrollAdjustment; horizontal?: ScrollAdjustment } {
|
|
59
|
+
const style = getComputedStyle(el);
|
|
60
|
+
const vertical = SCROLLABLE_OVERFLOW.has(style.overflowY)
|
|
61
|
+
? { value: el.scrollTop, lower: 0, upper: el.scrollHeight, pageSize: el.clientHeight }
|
|
62
|
+
: undefined;
|
|
63
|
+
const horizontal = SCROLLABLE_OVERFLOW.has(style.overflowX)
|
|
64
|
+
? { value: el.scrollLeft, lower: 0, upper: el.scrollWidth, pageSize: el.clientWidth }
|
|
65
|
+
: undefined;
|
|
66
|
+
return { vertical, horizontal };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Whether a box scrolls on either axis at all. */
|
|
70
|
+
function isScroller(el: HTMLElement): boolean {
|
|
71
|
+
const style = getComputedStyle(el);
|
|
72
|
+
return SCROLLABLE_OVERFLOW.has(style.overflowY) || SCROLLABLE_OVERFLOW.has(style.overflowX);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* A wheel delta in CSS pixels, whatever unit the event chose to speak in.
|
|
77
|
+
*
|
|
78
|
+
* `deltaMode` is not decoration — Firefox reports `DOM_DELTA_LINE` (3-ish per
|
|
79
|
+
* detent) where Chrome reports pixels (100-ish per detent), so an unconverted
|
|
80
|
+
* delta makes the overshoot two orders of magnitude smaller on one browser. The
|
|
81
|
+
* line height is the conventional 16px default scaled by the browser's own
|
|
82
|
+
* three-lines-per-detent step.
|
|
83
|
+
*/
|
|
84
|
+
function wheelDeltaPx(event: WheelEvent, port: number): number {
|
|
85
|
+
if (event.deltaMode === WheelEvent.DOM_DELTA_LINE) return event.deltaY * 16;
|
|
86
|
+
if (event.deltaMode === WheelEvent.DOM_DELTA_PAGE) return event.deltaY * port;
|
|
87
|
+
return event.deltaY;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Which vertical edges may draw an undershoot — a toolbar view's own two flags. */
|
|
91
|
+
export interface AdwUndershootEdges {
|
|
92
|
+
/** `undershoot-top` on the view: the top bar is flat and was allocated a height. */
|
|
93
|
+
top: boolean;
|
|
94
|
+
/** `undershoot-bottom` on the view. */
|
|
95
|
+
bottom: boolean;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Keeps every scroller under a root marked with its live edge indicators.
|
|
100
|
+
*
|
|
101
|
+
* One instance per toolbar view. Scroll and wheel are listened for in the CAPTURE
|
|
102
|
+
* phase on the root: `scroll` does not bubble, but capture propagation runs down
|
|
103
|
+
* the whole path regardless, so one pair of listeners covers scrollers that appear
|
|
104
|
+
* later without re-binding per element.
|
|
105
|
+
*/
|
|
106
|
+
export class AdwScrollShading {
|
|
107
|
+
private _root: HTMLElement;
|
|
108
|
+
private _mutations: MutationObserver | null = null;
|
|
109
|
+
private _resize: ResizeObserver | null = null;
|
|
110
|
+
/** The unclamped position per scroller, i.e. `priv->unclamped_vadj_value`. */
|
|
111
|
+
private _unclamped = new WeakMap<HTMLElement, number>();
|
|
112
|
+
private _settleTimers = new WeakMap<HTMLElement, ReturnType<typeof setTimeout>>();
|
|
113
|
+
/** Every scroller currently marked, so teardown and rescans can clear them. */
|
|
114
|
+
private _shaded = new Set<HTMLElement>();
|
|
115
|
+
private _pendingScan = 0;
|
|
116
|
+
private _undershootEdges: AdwUndershootEdges = { top: true, bottom: true };
|
|
117
|
+
|
|
118
|
+
constructor(root: HTMLElement) {
|
|
119
|
+
this._root = root;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Which edges may draw an UNDERSHOOT. The overshoot glow is not gated: upstream
|
|
124
|
+
* draws it on every `scrolledwindow`, while the undershoot is what a toolbar
|
|
125
|
+
* view turns on for a flat bar and off for a raised one, which has its own
|
|
126
|
+
* shadow (`update_undershoots`).
|
|
127
|
+
*/
|
|
128
|
+
setUndershootEdges(edges: AdwUndershootEdges): void {
|
|
129
|
+
if (edges.top === this._undershootEdges.top && edges.bottom === this._undershootEdges.bottom) return;
|
|
130
|
+
this._undershootEdges = { ...edges };
|
|
131
|
+
for (const el of this._shaded) this._update(el);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** Start observing. Idempotent. */
|
|
135
|
+
connect(): void {
|
|
136
|
+
if (this._mutations) return;
|
|
137
|
+
this._root.addEventListener('scroll', this._onScroll, { capture: true, passive: true });
|
|
138
|
+
this._root.addEventListener('wheel', this._onWheel, { capture: true, passive: true });
|
|
139
|
+
// A rescan reads the computed overflow of every descendant, so it is coalesced
|
|
140
|
+
// to one per frame: replacing a story fires a mutation record per node.
|
|
141
|
+
this._mutations = new MutationObserver(() => this._scheduleRefresh());
|
|
142
|
+
this._mutations.observe(this._root, { childList: true, subtree: true });
|
|
143
|
+
this._resize = new ResizeObserver(() => this._scheduleRefresh());
|
|
144
|
+
this._resize.observe(this._root);
|
|
145
|
+
this.refresh();
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/** Stop observing and clear every class this instance set. */
|
|
149
|
+
disconnect(): void {
|
|
150
|
+
this._root.removeEventListener('scroll', this._onScroll, { capture: true });
|
|
151
|
+
this._root.removeEventListener('wheel', this._onWheel, { capture: true });
|
|
152
|
+
this._mutations?.disconnect();
|
|
153
|
+
this._mutations = null;
|
|
154
|
+
this._resize?.disconnect();
|
|
155
|
+
this._resize = null;
|
|
156
|
+
if (this._pendingScan) cancelAnimationFrame(this._pendingScan);
|
|
157
|
+
this._pendingScan = 0;
|
|
158
|
+
for (const el of this._shaded) this._clear(el);
|
|
159
|
+
this._shaded.clear();
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
private _scheduleRefresh(): void {
|
|
163
|
+
if (this._pendingScan) return;
|
|
164
|
+
this._pendingScan = requestAnimationFrame(() => {
|
|
165
|
+
this._pendingScan = 0;
|
|
166
|
+
this.refresh();
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/** Re-scan for scrollers and re-derive every indicator. */
|
|
171
|
+
refresh(): void {
|
|
172
|
+
const found = new Set<HTMLElement>();
|
|
173
|
+
// The ROOT counts: a toolbar view whose content area scrolls on its own is
|
|
174
|
+
// the common case, and `querySelectorAll` never returns the element it was
|
|
175
|
+
// called on.
|
|
176
|
+
if (isScroller(this._root)) found.add(this._root);
|
|
177
|
+
for (const el of this._root.querySelectorAll<HTMLElement>('*')) {
|
|
178
|
+
if (isScroller(el)) found.add(el);
|
|
179
|
+
}
|
|
180
|
+
for (const el of this._shaded) {
|
|
181
|
+
if (!found.has(el)) this._clear(el);
|
|
182
|
+
}
|
|
183
|
+
this._shaded = found;
|
|
184
|
+
for (const el of found) this._update(el);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
private _clear(el: HTMLElement): void {
|
|
188
|
+
el.classList.remove(SHADED_CLASS, ...ADW_UNDERSHOOT_CLASSES, ...OVERSHOOT_CLASSES);
|
|
189
|
+
el.style.removeProperty(OVERSHOOT_PROPERTY);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/** Re-derive the undershoot classes of one scroller from its current position. */
|
|
193
|
+
private _update(el: HTMLElement): void {
|
|
194
|
+
el.classList.add(SHADED_CLASS);
|
|
195
|
+
const live = scrollUndershootClasses(adjustmentsOf(el));
|
|
196
|
+
for (const cls of ADW_UNDERSHOOT_CLASSES) {
|
|
197
|
+
const allowed =
|
|
198
|
+
(cls !== 'undershoot-top' || this._undershootEdges.top) &&
|
|
199
|
+
(cls !== 'undershoot-bottom' || this._undershootEdges.bottom);
|
|
200
|
+
el.classList.toggle(cls, allowed && live.includes(cls));
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
private _onScroll = (event: Event): void => {
|
|
205
|
+
const el = event.target;
|
|
206
|
+
if (el instanceof HTMLElement && this._shaded.has(el)) this._update(el);
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* The overshoot half. A browser scroll container clamps at its edge and reports
|
|
211
|
+
* nothing further, so the push past it is read off the wheel deltas the
|
|
212
|
+
* container could not consume — which is exactly the value upstream keeps in
|
|
213
|
+
* `unclamped_vadj_value` and clamps one overshoot distance out.
|
|
214
|
+
*/
|
|
215
|
+
private _onWheel = (event: WheelEvent): void => {
|
|
216
|
+
const el = event.target instanceof HTMLElement ? this._scrollerFor(event.target) : null;
|
|
217
|
+
if (!el) return;
|
|
218
|
+
const { vertical } = adjustmentsOf(el);
|
|
219
|
+
if (!vertical) return;
|
|
220
|
+
|
|
221
|
+
const previous = this._unclamped.get(el) ?? vertical.value;
|
|
222
|
+
// A scroll that landed back inside the range resets the accumulator: the
|
|
223
|
+
// position the user pushed from is the real one again.
|
|
224
|
+
const base = overshootDistance(previous, vertical) === 0 ? vertical.value : previous;
|
|
225
|
+
const next = accumulateOvershoot(base, wheelDeltaPx(event, vertical.pageSize), vertical);
|
|
226
|
+
this._unclamped.set(el, next);
|
|
227
|
+
this._applyOvershoot(el, overshootDistance(next, vertical));
|
|
228
|
+
|
|
229
|
+
// Spring back once the wheel STOPS, not between two detents of one gesture.
|
|
230
|
+
clearTimeout(this._settleTimers.get(el));
|
|
231
|
+
this._settleTimers.set(
|
|
232
|
+
el,
|
|
233
|
+
setTimeout(() => {
|
|
234
|
+
this._unclamped.set(el, el.scrollTop);
|
|
235
|
+
this._applyOvershoot(el, 0);
|
|
236
|
+
}, ADW_OVERSHOOT_SETTLE_MS),
|
|
237
|
+
);
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
private _applyOvershoot(el: HTMLElement, distance: number): void {
|
|
241
|
+
el.classList.toggle('overshoot-top', distance < 0);
|
|
242
|
+
el.classList.toggle('overshoot-bottom', distance > 0);
|
|
243
|
+
if (distance === 0) el.style.removeProperty(OVERSHOOT_PROPERTY);
|
|
244
|
+
else el.style.setProperty(OVERSHOOT_PROPERTY, `${Math.abs(distance)}px`);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/** The nearest shaded scroller at or above a wheel target, root included. */
|
|
248
|
+
private _scrollerFor(target: HTMLElement): HTMLElement | null {
|
|
249
|
+
for (let el: HTMLElement | null = target; el; el = el.parentElement) {
|
|
250
|
+
if (this._shaded.has(el)) return el;
|
|
251
|
+
if (el === this._root) break;
|
|
252
|
+
}
|
|
253
|
+
return null;
|
|
254
|
+
}
|
|
255
|
+
}
|
package/src/source-view/theme.ts
CHANGED
|
@@ -15,9 +15,21 @@ import { EditorView } from '@codemirror/view';
|
|
|
15
15
|
import { HighlightStyle } from '@codemirror/language';
|
|
16
16
|
import { tags } from '@lezer/highlight';
|
|
17
17
|
|
|
18
|
-
/**
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Monospace stack — the `--monospace-font-family` token, with its own value
|
|
20
|
+
* repeated as the `var()` fallback for a page that loads `@gjsify/adwaita-web`'s
|
|
21
|
+
* stylesheet without the theme tokens.
|
|
22
|
+
*
|
|
23
|
+
* It USED to be a second stack, written out here and longer (Cascadia Code,
|
|
24
|
+
* JetBrains Mono, Source Code Pro, Consolas). Two consequences, both real: the
|
|
25
|
+
* `stylesheet-font-families` conformance rule reads `.css`/`.scss` only, so a
|
|
26
|
+
* stack living in a TS string literal was outside every check the repo has; and
|
|
27
|
+
* on a host with, say, JetBrains Mono but no Adwaita Mono, an `<adw-source-view>`
|
|
28
|
+
* and a `.monospace` label on the SAME page rendered in DIFFERENT typefaces.
|
|
29
|
+
* `_variables.scss` is where that stack is argued about (it explains why
|
|
30
|
+
* 'Cantarell' is dropped from it), so it is where it lives.
|
|
31
|
+
*/
|
|
32
|
+
const MONO_FONT = "var(--monospace-font-family, 'Adwaita Mono', ui-monospace, 'SF Mono', 'Menlo', monospace)";
|
|
21
33
|
|
|
22
34
|
/** The CodeMirror EditorView theme mapping `.cm-*` chrome to Adwaita tokens. */
|
|
23
35
|
export const adwaitaEditorTheme = EditorView.theme({
|
package/src/split-button.spec.ts
CHANGED
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
} from '@gjsify/adwaita-core/conformance';
|
|
20
20
|
import type { SplitButtonProperty } from '@gjsify/adwaita-core';
|
|
21
21
|
import type { AdwSplitButton } from './elements/adw-split-button.js';
|
|
22
|
+
import { fallbackMask, maskOf } from './icon-registry.spec.js';
|
|
22
23
|
|
|
23
24
|
/** The GObject properties the element re-emits as `notify::*` CustomEvents. */
|
|
24
25
|
const NOTIFY_PROPERTIES: readonly SplitButtonProperty[] = [
|
|
@@ -160,12 +161,15 @@ export const AdwSplitButtonTest = async () => {
|
|
|
160
161
|
host.remove();
|
|
161
162
|
});
|
|
162
163
|
|
|
163
|
-
await it('
|
|
164
|
+
await it('turns a stray icon name into the broken glyph, not a stray class', () => {
|
|
164
165
|
const { el, host } = mount({ 'icon-name': 'a b' });
|
|
165
166
|
const icon = actionHalf(el).querySelector('.adw-icon') as HTMLElement;
|
|
166
167
|
// `adw-icon--a b` would have added a second, unrelated `b` class.
|
|
167
168
|
expect(icon.classList.contains('b')).toBe(false);
|
|
168
|
-
expect(icon.className).toBe('adw-icon');
|
|
169
|
+
expect(icon.className).toBe('adw-icon adw-icon--image-missing');
|
|
170
|
+
// The name was GIVEN, so the slot shows what GTK's failed lookup shows —
|
|
171
|
+
// dropping the class left a visible 16px hole instead.
|
|
172
|
+
expect(maskOf(icon)).toBe(fallbackMask());
|
|
169
173
|
host.remove();
|
|
170
174
|
});
|
|
171
175
|
|
|
@@ -83,6 +83,12 @@ export const DOCUMENTED_STYLE_CLASSES: Record<string, { shipped: boolean; reason
|
|
|
83
83
|
monospace: SHIPPED,
|
|
84
84
|
numeric: SHIPPED,
|
|
85
85
|
// Colors
|
|
86
|
+
// `accent` was missing from this list for as long as it has existed, and not by
|
|
87
|
+
// judgement: the gate that keeps the list honest read only the doc's BACKTICK
|
|
88
|
+
// spelling, and `.accent` is the one class upstream writes as `<tt>.accent</tt>`
|
|
89
|
+
// (style-classes.md:368). It has always been implemented (`scss/_labels.scss:53`);
|
|
90
|
+
// what was missing was anything that would notice if it stopped being.
|
|
91
|
+
accent: SHIPPED,
|
|
86
92
|
error: SHIPPED,
|
|
87
93
|
warning: SHIPPED,
|
|
88
94
|
success: SHIPPED,
|