@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.
- package/dist/adwaita-web.css +835 -299
- 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-combo-row.d.ts +10 -0
- 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 +73 -11
- package/lib/types/elements/adw-menu-button.d.ts +6 -6
- package/lib/types/elements/adw-navigation-split-view.d.ts +48 -0
- package/lib/types/elements/adw-navigation-view.d.ts +63 -20
- package/lib/types/elements/adw-overlay-split-view.d.ts +94 -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-spinner.d.ts +23 -0
- 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 +39 -19
- package/lib/types/elements/adw-view-switcher-bar.d.ts +25 -7
- package/lib/types/elements/adw-view-switcher.d.ts +76 -13
- package/lib/types/elements/adw-window-title.d.ts +3 -1
- package/lib/types/elements/adw-wrap-box.d.ts +58 -3
- 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/_combo_row.scss +19 -0
- 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/_navigation_split_view.scss +47 -8
- package/scss/_overlay_split_view.scss +117 -32
- 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 +37 -19
- 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 +63 -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 +65 -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 +176 -0
- package/src/adw-banner.spec.ts +152 -0
- package/src/adw-button-content.spec.ts +207 -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-row-state.spec.ts +49 -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 +341 -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 +400 -0
- package/src/elements/adw-about-dialog.ts +223 -163
- package/src/elements/adw-action-row.ts +131 -11
- 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 +32 -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 +325 -131
- package/src/elements/adw-menu-button.ts +93 -70
- package/src/elements/adw-navigation-split-view.ts +220 -14
- package/src/elements/adw-navigation-view.ts +286 -148
- package/src/elements/adw-overlay-split-view.ts +536 -28
- 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 +168 -9
- 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 +700 -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 +183 -85
- package/src/elements/adw-view-switcher-bar.ts +171 -81
- package/src/elements/adw-view-switcher.ts +328 -117
- package/src/elements/adw-window-title.ts +44 -11
- package/src/elements/adw-wrap-box.ts +329 -78
- 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 +893 -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 +665 -0
|
@@ -1,70 +1,274 @@
|
|
|
1
1
|
// <adw-wrap-box> — A box-like container that lays its children out in a line and
|
|
2
|
-
// wraps them onto new lines when they run out of room (the web analog of
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
// wrap-reverse
|
|
19
|
-
//
|
|
20
|
-
//
|
|
2
|
+
// wraps them onto new lines when they run out of room (the web analog of flexbox
|
|
3
|
+
// `flex-wrap: wrap` with a gap). Mirrors Adw.WrapBox.
|
|
4
|
+
//
|
|
5
|
+
// Attributes — all fourteen of the widget's properties, each with a
|
|
6
|
+
// `notify::<property>` (adw-wrap-box.c:284-497). The element used to carry nine
|
|
7
|
+
// attributes and notify for two, so a consumer binding to any of the other
|
|
8
|
+
// twelve was listening to a signal that could not fire.
|
|
9
|
+
//
|
|
10
|
+
// child-spacing / line-spacing (int >= 0, default 0) -> column-gap / row-gap
|
|
11
|
+
// child-spacing-unit / line-spacing-unit ("px" | "pt" | "sp", default "px")
|
|
12
|
+
// orientation ("horizontal" | "vertical", default "horizontal")
|
|
13
|
+
// pack-direction ("start-to-end" | "end-to-start", default "start-to-end")
|
|
14
|
+
// align (0..1, default 0) — MAIN-axis offset of the whole line block
|
|
15
|
+
// justify ("none" | "fill" | "spread", default "none")
|
|
16
|
+
// justify-last-line (boolean)
|
|
17
|
+
// line-homogeneous (boolean)
|
|
18
|
+
// wrap-reverse (boolean)
|
|
19
|
+
// wrap-policy ("minimum" | "natural", default "natural")
|
|
20
|
+
// natural-line-length (int >= -1, default -1 = unset) + -unit
|
|
21
|
+
//
|
|
22
|
+
// The property CONTRACT — the normalisers and the justify/align/last-line
|
|
23
|
+
// decision — is `@gjsify/adwaita-core`'s `wrap-box.ts`, held to
|
|
24
|
+
// `WRAP_BOX_LINE_VECTORS` and friends, which the NativeScript suite drives too.
|
|
25
|
+
// This element used to carry its own copy of all of it.
|
|
26
|
+
//
|
|
21
27
|
// Reference: refs/libadwaita/src/adw-wrap-box.c / adw-wrap-layout.c (Adw.WrapBox)
|
|
22
28
|
// Reference: refs/adwaita-web/adwaita-web/scss/_wrap_box.scss
|
|
23
29
|
// Copyright (c) GNOME contributors (libadwaita). LGPLv2.1+.
|
|
24
30
|
// Modifications: Implemented as a Web Component for @gjsify/adwaita-web.
|
|
25
31
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
+
import {
|
|
33
|
+
ADW_WRAP_BOX_NATURAL_LINE_LENGTH_UNSET,
|
|
34
|
+
normalizeNaturalLineLength,
|
|
35
|
+
normalizeWrapBoxAlign,
|
|
36
|
+
normalizeWrapBoxJustify,
|
|
37
|
+
normalizeWrapBoxLengthUnit,
|
|
38
|
+
normalizeWrapBoxPackDirection,
|
|
39
|
+
normalizeWrapBoxSpacing,
|
|
40
|
+
normalizeWrapPolicy,
|
|
41
|
+
resolveWrapBoxChildOrder,
|
|
42
|
+
resolveWrapBoxLine,
|
|
43
|
+
wrapBoxLengthToPx,
|
|
44
|
+
wrapPolicyFlexShrink,
|
|
45
|
+
type AdwLengthUnit,
|
|
46
|
+
} from '@gjsify/adwaita-core';
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* The attributes that carry a property, in the order the C installs them.
|
|
50
|
+
*
|
|
51
|
+
* Doubles as the `notify::` roster: every entry gets an event on a real change,
|
|
52
|
+
* so adding an attribute without its notification is not expressible here.
|
|
53
|
+
*/
|
|
54
|
+
const PROPERTY_ATTRIBUTES = [
|
|
55
|
+
'child-spacing',
|
|
56
|
+
'child-spacing-unit',
|
|
57
|
+
'pack-direction',
|
|
58
|
+
'align',
|
|
59
|
+
'justify',
|
|
60
|
+
'justify-last-line',
|
|
61
|
+
'line-spacing',
|
|
62
|
+
'line-spacing-unit',
|
|
63
|
+
'line-homogeneous',
|
|
64
|
+
'natural-line-length',
|
|
65
|
+
'natural-line-length-unit',
|
|
66
|
+
'wrap-reverse',
|
|
67
|
+
'wrap-policy',
|
|
68
|
+
'orientation',
|
|
69
|
+
] as const;
|
|
70
|
+
|
|
71
|
+
/** One attribute's normalised value, for the "did it really change" comparison. */
|
|
72
|
+
type PropertyValue = string | number | boolean;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* `align` as a `justify-content` keyword.
|
|
76
|
+
*
|
|
77
|
+
* C offsets the whole line block by `roundf (length_delta * align)` — a
|
|
78
|
+
* continuum. Flexbox has three main-axis positions, so the nearest one is taken;
|
|
79
|
+
* that is the renderer's approximation, not libadwaita's rule, which is why it
|
|
80
|
+
* lives here and not in the conformance table.
|
|
81
|
+
*/
|
|
82
|
+
function alignToJustifyContent(align: number): string {
|
|
83
|
+
if (align < 0.25) return 'flex-start';
|
|
84
|
+
if (align < 0.75) return 'center';
|
|
85
|
+
return 'flex-end';
|
|
86
|
+
}
|
|
32
87
|
|
|
33
88
|
export class AdwWrapBox extends HTMLElement {
|
|
34
89
|
private _initialized = false;
|
|
35
90
|
|
|
36
91
|
static get observedAttributes() {
|
|
37
|
-
return [
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
'justify-last-line',
|
|
44
|
-
'line-homogeneous',
|
|
45
|
-
'pack-direction',
|
|
46
|
-
'wrap-reverse',
|
|
47
|
-
];
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/** Spacing between children on the same line, in px (CSS column-gap). */
|
|
92
|
+
return [...PROPERTY_ATTRIBUTES];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// --- properties ---
|
|
96
|
+
|
|
97
|
+
/** Spacing between children on the same line, in `child-spacing-unit`. */
|
|
51
98
|
get childSpacing(): number {
|
|
52
|
-
return
|
|
99
|
+
return normalizeWrapBoxSpacing(this.getAttribute('child-spacing'));
|
|
53
100
|
}
|
|
54
101
|
|
|
55
102
|
set childSpacing(value: number) {
|
|
56
103
|
this.setAttribute('child-spacing', String(value));
|
|
57
104
|
}
|
|
58
105
|
|
|
59
|
-
/**
|
|
106
|
+
/** The unit `child-spacing` is written in. Defaults to `px`, as in C. */
|
|
107
|
+
get childSpacingUnit(): AdwLengthUnit {
|
|
108
|
+
return normalizeWrapBoxLengthUnit(this.getAttribute('child-spacing-unit'));
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
set childSpacingUnit(value: AdwLengthUnit) {
|
|
112
|
+
this.setAttribute('child-spacing-unit', value);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** Spacing between lines, in `line-spacing-unit`. */
|
|
60
116
|
get lineSpacing(): number {
|
|
61
|
-
return
|
|
117
|
+
return normalizeWrapBoxSpacing(this.getAttribute('line-spacing'));
|
|
62
118
|
}
|
|
63
119
|
|
|
64
120
|
set lineSpacing(value: number) {
|
|
65
121
|
this.setAttribute('line-spacing', String(value));
|
|
66
122
|
}
|
|
67
123
|
|
|
124
|
+
/** The unit `line-spacing` is written in. Defaults to `px`, as in C. */
|
|
125
|
+
get lineSpacingUnit(): AdwLengthUnit {
|
|
126
|
+
return normalizeWrapBoxLengthUnit(this.getAttribute('line-spacing-unit'));
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
set lineSpacingUnit(value: AdwLengthUnit) {
|
|
130
|
+
this.setAttribute('line-spacing-unit', value);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** Where the children sit ALONG the line (main axis): 0 start, 0.5 middle, 1 end. */
|
|
134
|
+
get align(): number {
|
|
135
|
+
return normalizeWrapBoxAlign(this.getAttribute('align'));
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
set align(value: number) {
|
|
139
|
+
this.setAttribute('align', String(value));
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** Whether and how each line is stretched to fill the widget. */
|
|
143
|
+
get justify(): 'none' | 'fill' | 'spread' {
|
|
144
|
+
return normalizeWrapBoxJustify(this.getAttribute('justify'));
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
set justify(value: 'none' | 'fill' | 'spread') {
|
|
148
|
+
this.setAttribute('justify', value);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** Whether the FINAL line is justified too. Default false, as in C. */
|
|
152
|
+
get justifyLastLine(): boolean {
|
|
153
|
+
return this.hasAttribute('justify-last-line');
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
set justifyLastLine(value: boolean) {
|
|
157
|
+
this.toggleAttribute('justify-last-line', !!value);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/** Whether an overflowing line squeezes its children or lets them spill. */
|
|
161
|
+
get wrapPolicy(): 'minimum' | 'natural' {
|
|
162
|
+
return normalizeWrapPolicy(this.getAttribute('wrap-policy'));
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
set wrapPolicy(value: 'minimum' | 'natural') {
|
|
166
|
+
this.setAttribute('wrap-policy', value);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** The direction children are packed in each line. */
|
|
170
|
+
get packDirection(): 'start-to-end' | 'end-to-start' {
|
|
171
|
+
return normalizeWrapBoxPackDirection(this.getAttribute('pack-direction'));
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
set packDirection(value: 'start-to-end' | 'end-to-start') {
|
|
175
|
+
this.setAttribute('pack-direction', value);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/** Whether lines wrap upwards (horizontal) / towards the start (vertical). */
|
|
179
|
+
get wrapReverse(): boolean {
|
|
180
|
+
return this.hasAttribute('wrap-reverse');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
set wrapReverse(value: boolean) {
|
|
184
|
+
this.toggleAttribute('wrap-reverse', !!value);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** Whether every line takes the same amount of space. */
|
|
188
|
+
get lineHomogeneous(): boolean {
|
|
189
|
+
return this.hasAttribute('line-homogeneous');
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
set lineHomogeneous(value: boolean) {
|
|
193
|
+
this.toggleAttribute('line-homogeneous', !!value);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/** The natural line length in `natural-line-length-unit`, or `-1` when unset. */
|
|
197
|
+
get naturalLineLength(): number {
|
|
198
|
+
return normalizeNaturalLineLength(this.getAttribute('natural-line-length'));
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
set naturalLineLength(value: number) {
|
|
202
|
+
this.setAttribute('natural-line-length', String(value));
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/** The unit `natural-line-length` is written in. Defaults to `px`, as in C. */
|
|
206
|
+
get naturalLineLengthUnit(): AdwLengthUnit {
|
|
207
|
+
return normalizeWrapBoxLengthUnit(this.getAttribute('natural-line-length-unit'));
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
set naturalLineLengthUnit(value: AdwLengthUnit) {
|
|
211
|
+
this.setAttribute('natural-line-length-unit', value);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/** The axis children are packed along. */
|
|
215
|
+
get orientation(): 'horizontal' | 'vertical' {
|
|
216
|
+
return this.getAttribute('orientation') === 'vertical' ? 'vertical' : 'horizontal';
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
set orientation(value: 'horizontal' | 'vertical') {
|
|
220
|
+
this.setAttribute('orientation', value);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// --- child list (adw_wrap_box_insert_child_after / reorder / remove_all) ---
|
|
224
|
+
//
|
|
225
|
+
// `adw_wrap_box_append`, `_prepend` and `_remove` are deliberately NOT
|
|
226
|
+
// redeclared: `ParentNode.append` / `.prepend` and `Node.removeChild` are
|
|
227
|
+
// those three, with the same semantics, and shadowing them would give this
|
|
228
|
+
// element two spellings of one operation. Only the three the DOM has no
|
|
229
|
+
// counterpart for are added.
|
|
230
|
+
|
|
231
|
+
/** The element children, which are what the layout actually lays out. */
|
|
232
|
+
private get _children(): Element[] {
|
|
233
|
+
return [...this.children];
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* `adw_wrap_box_insert_child_after` (adw-wrap-box.c:1283-1300).
|
|
238
|
+
*
|
|
239
|
+
* A NULL/absent `sibling` inserts at the FIRST position, not the last —
|
|
240
|
+
* `gtk_widget_insert_after`'s documented rule, pinned by
|
|
241
|
+
* `WRAP_BOX_CHILD_ORDER_VECTORS`. Returns whether the insert happened; a
|
|
242
|
+
* refusal is where C would have hit a `g_return_if_fail`.
|
|
243
|
+
*/
|
|
244
|
+
insertChildAfter(child: Element, sibling: Element | null = null): boolean {
|
|
245
|
+
return this._applyOrder('insert-after', child, sibling);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/** `adw_wrap_box_reorder_child_after` (adw-wrap-box.c:1315-1332). Same NULL rule. */
|
|
249
|
+
reorderChildAfter(child: Element, sibling: Element | null = null): boolean {
|
|
250
|
+
return this._applyOrder('reorder-after', child, sibling);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/** `adw_wrap_box_remove_all` (adw-wrap-box.c:1406-1414). */
|
|
254
|
+
removeAll(): void {
|
|
255
|
+
for (const child of this._children) this.removeChild(child);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
private _applyOrder(op: 'insert-after' | 'reorder-after', child: Element, sibling: Element | null): boolean {
|
|
259
|
+
const next = resolveWrapBoxChildOrder({ children: this._children, child, sibling, op });
|
|
260
|
+
if (next === null) return false;
|
|
261
|
+
// Move exactly the one node: `insertBefore` relocates a node already in
|
|
262
|
+
// the tree, and a null reference node appends. Re-appending the whole
|
|
263
|
+
// resolved list would reach the same order while detaching and
|
|
264
|
+
// re-attaching every sibling, which resets focus and any element state.
|
|
265
|
+
const at = next.indexOf(child);
|
|
266
|
+
this.insertBefore(child, next[at + 1] ?? null);
|
|
267
|
+
return true;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// --- lifecycle ---
|
|
271
|
+
|
|
68
272
|
connectedCallback() {
|
|
69
273
|
if (this._initialized) return;
|
|
70
274
|
this._initialized = true;
|
|
@@ -74,41 +278,58 @@ export class AdwWrapBox extends HTMLElement {
|
|
|
74
278
|
attributeChangedCallback(name: string, old: string | null, value: string | null) {
|
|
75
279
|
if (!this._initialized) return;
|
|
76
280
|
this._sync();
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
281
|
+
// C compares AFTER normalising and returns early when nothing changed
|
|
282
|
+
// (adw-wrap-box.c:592-593), so `child-spacing="-5"` on a box already at 0
|
|
283
|
+
// notifies nobody. The same gate applies to every property, not just the
|
|
284
|
+
// two spacings this element used to special-case.
|
|
285
|
+
const next = this._normalized(name, value);
|
|
286
|
+
if (next === this._normalized(name, old)) return;
|
|
287
|
+
this.dispatchEvent(new CustomEvent(`notify::${name}`, { bubbles: true, detail: { [name]: next } }));
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/** One attribute's value as the widget stores it — the notify comparison key. */
|
|
291
|
+
private _normalized(name: string, raw: string | null): PropertyValue {
|
|
292
|
+
switch (name) {
|
|
293
|
+
case 'child-spacing':
|
|
294
|
+
case 'line-spacing':
|
|
295
|
+
return normalizeWrapBoxSpacing(raw);
|
|
296
|
+
case 'child-spacing-unit':
|
|
297
|
+
case 'line-spacing-unit':
|
|
298
|
+
case 'natural-line-length-unit':
|
|
299
|
+
return normalizeWrapBoxLengthUnit(raw);
|
|
300
|
+
case 'align':
|
|
301
|
+
return normalizeWrapBoxAlign(raw);
|
|
302
|
+
case 'justify':
|
|
303
|
+
return normalizeWrapBoxJustify(raw);
|
|
304
|
+
case 'wrap-policy':
|
|
305
|
+
return normalizeWrapPolicy(raw);
|
|
306
|
+
case 'pack-direction':
|
|
307
|
+
return normalizeWrapBoxPackDirection(raw);
|
|
308
|
+
case 'natural-line-length':
|
|
309
|
+
return normalizeNaturalLineLength(raw);
|
|
310
|
+
case 'orientation':
|
|
311
|
+
return raw === 'vertical' ? 'vertical' : 'horizontal';
|
|
312
|
+
default:
|
|
313
|
+
// The three booleans: a present attribute is true whatever it says.
|
|
314
|
+
return raw !== null;
|
|
92
315
|
}
|
|
93
316
|
}
|
|
94
317
|
|
|
95
318
|
private _sync() {
|
|
96
319
|
const style = this.style;
|
|
97
|
-
const vertical = this.
|
|
98
|
-
const reverseWrap = this.hasAttribute('wrap-reverse');
|
|
99
|
-
const packEndToStart = this.getAttribute('pack-direction') === 'end-to-start';
|
|
320
|
+
const vertical = this.orientation === 'vertical';
|
|
100
321
|
|
|
101
322
|
// Main axis is the packing direction; lines wrap along the cross axis.
|
|
102
323
|
const direction = vertical ? 'column' : 'row';
|
|
103
|
-
|
|
104
|
-
style.
|
|
105
|
-
style.flexWrap = reverseWrap ? 'wrap-reverse' : 'wrap';
|
|
324
|
+
style.flexDirection = this.packDirection === 'end-to-start' ? `${direction}-reverse` : direction;
|
|
325
|
+
style.flexWrap = this.wrapReverse ? 'wrap-reverse' : 'wrap';
|
|
106
326
|
|
|
107
327
|
// child-spacing is along the line (main axis), line-spacing is between
|
|
108
328
|
// lines (cross axis). For a horizontal box that maps to column-gap /
|
|
109
|
-
// row-gap respectively; the mapping flips for a vertical box.
|
|
110
|
-
|
|
111
|
-
const
|
|
329
|
+
// row-gap respectively; the mapping flips for a vertical box. Both are
|
|
330
|
+
// resolved through their OWN unit first, which is why they can disagree.
|
|
331
|
+
const childGap = `${wrapBoxLengthToPx(this.childSpacing, this.childSpacingUnit)}px`;
|
|
332
|
+
const lineGap = `${wrapBoxLengthToPx(this.lineSpacing, this.lineSpacingUnit)}px`;
|
|
112
333
|
if (vertical) {
|
|
113
334
|
style.rowGap = childGap;
|
|
114
335
|
style.columnGap = lineGap;
|
|
@@ -116,23 +337,53 @@ export class AdwWrapBox extends HTMLElement {
|
|
|
116
337
|
style.columnGap = childGap;
|
|
117
338
|
style.rowGap = lineGap;
|
|
118
339
|
}
|
|
340
|
+
// Read back by the last-line filler, which has to cancel exactly one
|
|
341
|
+
// main-axis gap so it can never push a full line into a new one.
|
|
342
|
+
style.setProperty('--adw-wrap-box-child-spacing', childGap);
|
|
343
|
+
|
|
344
|
+
const justify = this.justify;
|
|
345
|
+
const justifyLastLine = this.justifyLastLine;
|
|
346
|
+
const align = this.align;
|
|
119
347
|
|
|
120
|
-
//
|
|
121
|
-
//
|
|
122
|
-
|
|
123
|
-
|
|
348
|
+
// A flex container has ONE `justify-content` and applies it to every
|
|
349
|
+
// line, so it carries the COMPLETE-line rule; the final-line rule is
|
|
350
|
+
// published beside it for the stylesheet, which reaches that line through
|
|
351
|
+
// the only two selectors CSS offers — `:only-child` (a box with one child
|
|
352
|
+
// has one line, and it is the last) and the generated `::after` filler.
|
|
353
|
+
// The pair is deliberately never collapsed into one attribute: flexbox
|
|
354
|
+
// cannot express "every line but the last", and pretending otherwise is
|
|
355
|
+
// what made `justify-last-line` a no-op in the first place.
|
|
356
|
+
const line = resolveWrapBoxLine({ justify, justifyLastLine, align, lastLine: false, childrenInLine: 2 });
|
|
357
|
+
const lastLine = resolveWrapBoxLine({ justify, justifyLastLine, align, lastLine: true, childrenInLine: 2 });
|
|
358
|
+
this.dataset.lineJustify = line.justify;
|
|
359
|
+
this.dataset.lastLineJustify = lastLine.justify;
|
|
124
360
|
|
|
125
|
-
//
|
|
126
|
-
//
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
if (align >= 0.75) crossAlign = 'flex-end';
|
|
130
|
-
else if (align >= 0.25) crossAlign = 'center';
|
|
131
|
-
style.alignItems = crossAlign;
|
|
361
|
+
// SPREAD widens the gaps; FILL grows the children (stylesheet) and leaves
|
|
362
|
+
// the gaps alone; NONE offsets the whole line block by `align` — along
|
|
363
|
+
// the MAIN axis, never the cross one.
|
|
364
|
+
style.justifyContent = line.growGaps ? 'space-between' : alignToJustifyContent(line.align);
|
|
132
365
|
|
|
133
366
|
// line-homogeneous makes every line take the same amount of space, which
|
|
134
367
|
// flexbox approximates by stretching the lines across the cross axis.
|
|
135
|
-
style.alignContent = this.
|
|
368
|
+
style.alignContent = this.lineHomogeneous ? 'stretch' : 'flex-start';
|
|
369
|
+
|
|
370
|
+
// `wrap-policy` decides whether an overflowing line squeezes its children
|
|
371
|
+
// or lets them spill. CSS defaults `flex-shrink` to 1, i.e. to
|
|
372
|
+
// ADW_WRAP_MINIMUM — the value libadwaita does NOT default to, so an
|
|
373
|
+
// unset policy has been drawing the wrong one on every wrap box. What
|
|
374
|
+
// flexbox cannot do either way is pack MORE children onto a line, since
|
|
375
|
+
// line breaking runs on hypothetical sizes; see `wrapPolicyFlexShrink`.
|
|
376
|
+
style.setProperty('--adw-wrap-box-child-shrink', String(wrapPolicyFlexShrink(this.wrapPolicy)));
|
|
377
|
+
|
|
378
|
+
// DELIBERATE DEVIATION: libadwaita's `natural-line-length` caps the box's
|
|
379
|
+
// NATURAL size request, leaving a larger allocation free to happen; CSS
|
|
380
|
+
// has no property that caps only the intrinsic contribution, so this is a
|
|
381
|
+
// max-size on the main axis and therefore caps the allocation too. It is
|
|
382
|
+
// the intended use (limiting line length inside a popover) either way.
|
|
383
|
+
const natural = wrapBoxLengthToPx(this.naturalLineLength, this.naturalLineLengthUnit);
|
|
384
|
+
const cap = natural === ADW_WRAP_BOX_NATURAL_LINE_LENGTH_UNSET ? '' : `${natural}px`;
|
|
385
|
+
style.maxWidth = vertical ? '' : cap;
|
|
386
|
+
style.maxHeight = vertical ? cap : '';
|
|
136
387
|
}
|
|
137
388
|
}
|
|
138
389
|
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// The DOM half of the view-switcher family — what `@gjsify/adwaita-core` cannot
|
|
2
|
+
// decide because it is genuinely per-renderer.
|
|
3
|
+
//
|
|
4
|
+
// Everything that decides WHAT a switcher shows (the button-visibility rule, the
|
|
5
|
+
// `image-missing` fallback, the badge label + screen-reader description, the
|
|
6
|
+
// mnemonic-stripped label, the tooltip, the reveal gate, the two index spaces)
|
|
7
|
+
// lives in `@gjsify/adwaita-core` and is pinned by the conformance vectors.
|
|
8
|
+
// What is left, and lives here, is how a derived model becomes nodes: an icon
|
|
9
|
+
// NAME turned into a CSS mask class, an indicator span, and a `setTimeout`
|
|
10
|
+
// wrapped in the scheduler seam the core takes for the drag-hover dwell.
|
|
11
|
+
//
|
|
12
|
+
// Shared by all three switcher elements because it was three copies before:
|
|
13
|
+
// `<adw-view-switcher>`, `<adw-inline-view-switcher>` and
|
|
14
|
+
// `<adw-view-switcher-bar>` each built their own icon span and their own page
|
|
15
|
+
// parsing, and the copies had already drifted (a titleless page rendered `''` in
|
|
16
|
+
// one and `page-<index>` in another).
|
|
17
|
+
//
|
|
18
|
+
// Reference: refs/libadwaita/src/adw-view-switcher.c
|
|
19
|
+
// Reference: refs/libadwaita/src/adw-indicator-bin.c
|
|
20
|
+
// Copyright (c) GNOME contributors (libadwaita). LGPLv2.1+.
|
|
21
|
+
// Modifications: Implemented for @gjsify/adwaita-web Web Components.
|
|
22
|
+
|
|
23
|
+
import { viewSwitcherIconName } from '@gjsify/adwaita-core';
|
|
24
|
+
import type { AdwViewSwitcherPageInit, ViewSwitcherScheduler, ViewSwitcherTimerHandle } from '@gjsify/adwaita-core';
|
|
25
|
+
|
|
26
|
+
import { type AdwIcon, createAdwIcon } from './adw-icon.js';
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The browser's timer behind the core's scheduler seam.
|
|
30
|
+
*
|
|
31
|
+
* One shared instance rather than one per element: it holds no state, and the
|
|
32
|
+
* seam exists so the CORE stays free of a global timer, not so each element
|
|
33
|
+
* invents its own.
|
|
34
|
+
*/
|
|
35
|
+
export const domViewSwitcherScheduler: ViewSwitcherScheduler = {
|
|
36
|
+
schedule(callback: () => void, ms: number): ViewSwitcherTimerHandle {
|
|
37
|
+
return setTimeout(callback, ms);
|
|
38
|
+
},
|
|
39
|
+
cancel(handle: ViewSwitcherTimerHandle): void {
|
|
40
|
+
clearTimeout(handle as ReturnType<typeof setTimeout>);
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Build a switcher button's icon node, carrying its own position class.
|
|
46
|
+
*
|
|
47
|
+
* The core resolves NULL/empty to `image-missing` (which the stylesheet carries
|
|
48
|
+
* a real glyph for); the `-symbolic` strip and the CSS-token guard are
|
|
49
|
+
* `normalizeIconName`'s, applied by `<adw-icon>` — that strip is this package's
|
|
50
|
+
* own mask-class convention and has no counterpart in C, where the name reaches
|
|
51
|
+
* `GtkImage` untouched.
|
|
52
|
+
*/
|
|
53
|
+
export function createSwitcherIcon(iconName: string, extra: string): AdwIcon {
|
|
54
|
+
return createAdwIcon(viewSwitcherIconName(iconName), extra);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Repaint an existing switcher icon from a page's declared name. */
|
|
58
|
+
export function applySwitcherIcon(icon: AdwIcon, iconName: string): void {
|
|
59
|
+
icon.iconName = viewSwitcherIconName(iconName);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Read a declared page element into the core's page init.
|
|
64
|
+
*
|
|
65
|
+
* `getAttribute` returns `null` for an absent attribute, which is EXACTLY C's
|
|
66
|
+
* NULL — so the values are passed through unchanged rather than defaulted to
|
|
67
|
+
* `''`. That `?? ''` is what made the button-visibility rule unimplementable in
|
|
68
|
+
* the old code: once an absent title has become the empty string, "the page has
|
|
69
|
+
* no title" is no longer expressible.
|
|
70
|
+
*/
|
|
71
|
+
export function readSwitcherPage(element: Element): AdwViewSwitcherPageInit {
|
|
72
|
+
const badge = Number.parseInt(element.getAttribute('badge-number') ?? '', 10);
|
|
73
|
+
return {
|
|
74
|
+
// A page with no `name` is the page named '' — the same choice
|
|
75
|
+
// `<adw-view-stack>` made, and for the same reason: C stores the name
|
|
76
|
+
// verbatim (adw-view-stack.c:1170) and an invented `page-<index>` is
|
|
77
|
+
// addressable by a name the author never wrote.
|
|
78
|
+
name: element.getAttribute('name') ?? '',
|
|
79
|
+
title: element.getAttribute('title'),
|
|
80
|
+
iconName: element.getAttribute('icon-name'),
|
|
81
|
+
// `hidden` is the DOM spelling of AdwViewStackPage:visible.
|
|
82
|
+
visible: !element.hasAttribute('hidden'),
|
|
83
|
+
useUnderline: element.hasAttribute('use-underline'),
|
|
84
|
+
badgeNumber: Number.isNaN(badge) ? 0 : badge,
|
|
85
|
+
needsAttention: element.hasAttribute('needs-attention'),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Paint `AdwIndicatorBin`'s badge/dot onto a span: the badge text when there is
|
|
91
|
+
* one, a bare dot when only `needs-attention` is set, nothing otherwise
|
|
92
|
+
* (adw-indicator-bin.c:58-68 + the `.needs-attention` style class).
|
|
93
|
+
*/
|
|
94
|
+
export function applyIndicator(indicator: HTMLElement, badgeLabel: string, needsAttention: boolean): void {
|
|
95
|
+
indicator.textContent = badgeLabel;
|
|
96
|
+
indicator.classList.toggle('needs-attention', needsAttention && badgeLabel.length === 0);
|
|
97
|
+
indicator.hidden = badgeLabel.length === 0 && !needsAttention;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Mirror the derived screen-reader description onto the element, REMOVING the
|
|
102
|
+
* attribute when there is none — `update_description_cb` resets the accessible
|
|
103
|
+
* property rather than setting it to `""` (adw-view-switcher-button.c:104-113).
|
|
104
|
+
*/
|
|
105
|
+
export function applyDescription(element: HTMLElement, description: string): void {
|
|
106
|
+
if (description) element.setAttribute('aria-description', description);
|
|
107
|
+
else element.removeAttribute('aria-description');
|
|
108
|
+
}
|