@gjsify/adwaita-web 0.30.0 → 0.32.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adwaita-web.css +786 -272
- package/dist/adwaita-web.css.map +1 -1
- package/lib/types/adw-about-dialog.spec.d.ts +1 -0
- package/lib/types/adw-action-rows.spec.d.ts +1 -0
- package/lib/types/adw-avatar.spec.d.ts +1 -0
- package/lib/types/adw-banner.spec.d.ts +1 -0
- package/lib/types/adw-button-content.spec.d.ts +1 -0
- package/lib/types/adw-carousel.spec.d.ts +1 -0
- package/lib/types/adw-checks.spec.d.ts +1 -0
- package/lib/types/adw-header-bar.spec.d.ts +1 -0
- package/lib/types/adw-icon.spec.d.ts +1 -0
- package/lib/types/adw-progress-bar.spec.d.ts +1 -0
- package/lib/types/adw-sidebar.spec.d.ts +1 -0
- package/lib/types/adw-switch.spec.d.ts +1 -0
- package/lib/types/adw-wrap-box.spec.d.ts +1 -0
- package/lib/types/bottom-sheet.spec.d.ts +1 -0
- package/lib/types/breakpoints.d.ts +22 -0
- package/lib/types/breakpoints.spec.d.ts +1 -0
- package/lib/types/chrome.spec.d.ts +1 -0
- package/lib/types/elements/adw-about-dialog.d.ts +47 -8
- package/lib/types/elements/adw-action-row.d.ts +35 -0
- package/lib/types/elements/adw-avatar.d.ts +11 -0
- package/lib/types/elements/adw-bottom-sheet.d.ts +33 -4
- package/lib/types/elements/adw-button-content.d.ts +12 -0
- package/lib/types/elements/adw-button-row.d.ts +10 -1
- package/lib/types/elements/adw-carousel.d.ts +111 -8
- package/lib/types/elements/adw-checks.d.ts +57 -0
- package/lib/types/elements/adw-clamp.d.ts +4 -1
- package/lib/types/elements/adw-data-grid.d.ts +11 -31
- package/lib/types/elements/adw-drop-down.d.ts +17 -20
- package/lib/types/elements/adw-entry-row.d.ts +93 -3
- package/lib/types/elements/adw-expander-row.d.ts +7 -1
- package/lib/types/elements/adw-header-bar.d.ts +15 -0
- package/lib/types/elements/adw-icon.d.ts +25 -0
- package/lib/types/elements/adw-inline-view-switcher.d.ts +37 -11
- package/lib/types/elements/adw-menu-button.d.ts +6 -6
- package/lib/types/elements/adw-navigation-split-view.d.ts +6 -0
- package/lib/types/elements/adw-navigation-view.d.ts +63 -20
- package/lib/types/elements/adw-overlay-split-view.d.ts +38 -0
- package/lib/types/elements/adw-password-entry-row.d.ts +19 -10
- package/lib/types/elements/adw-popover.d.ts +58 -0
- package/lib/types/elements/adw-preferences-dialog.d.ts +57 -2
- package/lib/types/elements/adw-preferences-group.d.ts +31 -0
- package/lib/types/elements/adw-progress-bar.d.ts +38 -0
- package/lib/types/elements/adw-sidebar.d.ts +37 -5
- package/lib/types/elements/adw-split-button.d.ts +53 -7
- package/lib/types/elements/adw-switch-row.d.ts +11 -2
- package/lib/types/elements/adw-switch.d.ts +15 -0
- package/lib/types/elements/adw-tab-view.d.ts +129 -12
- package/lib/types/elements/adw-toolbar-view.d.ts +5 -0
- package/lib/types/elements/adw-view-stack.d.ts +27 -19
- package/lib/types/elements/adw-view-switcher-bar.d.ts +23 -7
- package/lib/types/elements/adw-view-switcher.d.ts +22 -11
- package/lib/types/elements/adw-window-title.d.ts +3 -1
- package/lib/types/elements/view-switcher-dom.d.ts +44 -0
- package/lib/types/entry-rows.spec.d.ts +1 -0
- package/lib/types/index.d.ts +7 -0
- package/lib/types/navigation-view.spec.d.ts +1 -0
- package/lib/types/preferences-search.d.ts +20 -0
- package/lib/types/preferences.spec.d.ts +1 -0
- package/lib/types/split-button.spec.d.ts +1 -0
- package/lib/types/split-views.spec.d.ts +1 -0
- package/lib/types/styles.generated.d.ts +1 -1
- package/lib/types/tab-view.spec.d.ts +1 -0
- package/lib/types/view-stack.spec.d.ts +1 -0
- package/lib/types/view-switcher.spec.d.ts +1 -0
- package/package.json +6 -6
- package/scss/_banner.scss +13 -0
- package/scss/_bottom_sheet.scss +5 -6
- package/scss/_button.scss +3 -26
- package/scss/_button_content.scss +28 -0
- package/scss/_checks.scss +228 -0
- package/scss/_clamp.scss +9 -3
- package/scss/_data_grid.scss +15 -7
- package/scss/_drop_down.scss +12 -55
- package/scss/_expander_row.scss +5 -50
- package/scss/_icon.scss +51 -0
- package/scss/_icons.generated.scss +6 -0
- package/scss/_inline_view_switcher.scss +34 -0
- package/scss/_labels.scss +135 -0
- package/scss/_menu_button.scss +10 -70
- package/scss/_popover.scss +186 -0
- package/scss/_preferences.scss +29 -0
- package/scss/_preferences_dialog.scss +17 -0
- package/scss/_progress_bar.scss +161 -0
- package/scss/_reset.scss +18 -7
- package/scss/_spinner.scss +7 -3
- package/scss/_split_button.scss +6 -50
- package/scss/_switch.scss +90 -0
- package/scss/_switch_row.scss +6 -52
- package/scss/_tab_view.scss +64 -0
- package/scss/_theme.scss +10 -0
- package/scss/_toolbar_view.scss +74 -0
- package/scss/_variables.scss +34 -0
- package/scss/_view_switcher.scss +40 -0
- package/scss/_view_switcher_bar.scss +37 -0
- package/scss/_wrap_box.scss +56 -6
- package/scss/adwaita-skin.scss +23 -0
- package/src/adw-about-dialog.spec.ts +350 -0
- package/src/adw-action-rows.spec.ts +342 -0
- package/src/adw-avatar.spec.ts +139 -0
- package/src/adw-banner.spec.ts +152 -0
- package/src/adw-button-content.spec.ts +183 -0
- package/src/adw-button-row.spec.ts +12 -6
- package/src/adw-carousel.spec.ts +389 -0
- package/src/adw-checks.spec.ts +368 -0
- package/src/adw-drop-down.spec.ts +123 -0
- package/src/adw-header-bar.spec.ts +83 -0
- package/src/adw-icon.spec.ts +167 -0
- package/src/adw-progress-bar.spec.ts +173 -0
- package/src/adw-sidebar.spec.ts +332 -0
- package/src/adw-switch.spec.ts +242 -0
- package/src/adw-tab-view.spec.ts +12 -3
- package/src/adw-view-switcher-bar.spec.ts +61 -13
- package/src/adw-wrap-box.spec.ts +194 -0
- package/src/bottom-sheet.spec.ts +325 -0
- package/src/breakpoints.spec.ts +147 -0
- package/src/breakpoints.ts +67 -0
- package/src/chrome.spec.ts +301 -0
- package/src/elements/adw-about-dialog.ts +223 -163
- package/src/elements/adw-action-row.ts +120 -10
- package/src/elements/adw-avatar.ts +48 -54
- package/src/elements/adw-banner.ts +46 -10
- package/src/elements/adw-bottom-sheet.ts +145 -60
- package/src/elements/adw-button-content.ts +107 -33
- package/src/elements/adw-button-row.ts +61 -37
- package/src/elements/adw-button.ts +5 -7
- package/src/elements/adw-carousel.ts +467 -105
- package/src/elements/adw-checks.ts +299 -0
- package/src/elements/adw-clamp.ts +80 -10
- package/src/elements/adw-combo-row.ts +15 -7
- package/src/elements/adw-data-grid.ts +43 -91
- package/src/elements/adw-drop-down.ts +117 -128
- package/src/elements/adw-entry-row.ts +404 -36
- package/src/elements/adw-expander-row.ts +61 -29
- package/src/elements/adw-header-bar.ts +31 -6
- package/src/elements/adw-icon.ts +148 -0
- package/src/elements/adw-inline-view-switcher.ts +240 -129
- package/src/elements/adw-menu-button.ts +93 -70
- package/src/elements/adw-navigation-split-view.ts +48 -7
- package/src/elements/adw-navigation-view.ts +286 -148
- package/src/elements/adw-overlay-split-view.ts +164 -14
- package/src/elements/adw-password-entry-row.ts +111 -89
- package/src/elements/adw-popover.ts +283 -0
- package/src/elements/adw-preferences-dialog.ts +120 -21
- package/src/elements/adw-preferences-group.ts +128 -22
- package/src/elements/adw-progress-bar.ts +189 -0
- package/src/elements/adw-sidebar.ts +301 -142
- package/src/elements/adw-spin-row.ts +11 -5
- package/src/elements/adw-spinner.ts +23 -8
- package/src/elements/adw-split-button.ts +371 -92
- package/src/elements/adw-status-page.ts +9 -9
- package/src/elements/adw-switch-row.ts +100 -30
- package/src/elements/adw-switch.ts +129 -0
- package/src/elements/adw-tab-view.ts +682 -129
- package/src/elements/adw-toggle-group.ts +5 -7
- package/src/elements/adw-toolbar-view.ts +84 -21
- package/src/elements/adw-view-stack.ts +162 -85
- package/src/elements/adw-view-switcher-bar.ts +163 -81
- package/src/elements/adw-view-switcher.ts +231 -112
- package/src/elements/adw-window-title.ts +44 -11
- package/src/elements/adw-wrap-box.ts +131 -35
- package/src/elements/view-switcher-dom.ts +108 -0
- package/src/entry-rows.spec.ts +391 -0
- package/src/index.ts +23 -0
- package/src/navigation-view.spec.ts +435 -0
- package/src/preferences-search.ts +135 -0
- package/src/preferences.spec.ts +250 -0
- package/src/source-view/adw-source-view.ts +2 -5
- package/src/split-button.spec.ts +470 -0
- package/src/split-views.spec.ts +309 -0
- package/src/styles.generated.ts +1 -1
- package/src/tab-view.spec.ts +451 -0
- package/src/test.browser.mts +49 -0
- package/src/view-stack.spec.ts +272 -0
- package/src/view-switcher.spec.ts +446 -0
|
@@ -1,15 +1,51 @@
|
|
|
1
1
|
// <adw-switch-row> — Row with a title/subtitle and a trailing toggle switch.
|
|
2
2
|
// Attributes: title, subtitle, active (boolean)
|
|
3
3
|
// Events: notify::active (CustomEvent, mirrors GJS GObject signal naming)
|
|
4
|
+
//
|
|
5
|
+
// The ACTIVE flag, its notify rule and the toggle-on-row-activation are HEADLESS
|
|
6
|
+
// and live in `@gjsify/adwaita-core` (ADR 0004) as {@link SwitchRowState}; this
|
|
7
|
+
// element composes it and keeps only the DOM render half — the checkbox, the
|
|
8
|
+
// label column and the `notify::active` event.
|
|
9
|
+
// `@gjsify/adwaita-nativescript` composes the same state, so both ports share one
|
|
10
|
+
// behaviour. Two things this fixes, both from adw-switch-row.c:
|
|
11
|
+
// - a PROGRAMMATIC `row.active = true` now notifies. The DOM does not fire
|
|
12
|
+
// `change` for `.checked =`, so keying the event off the checkbox meant the
|
|
13
|
+
// row was silent — while the NativeScript port, whose `checkedChange` DOES
|
|
14
|
+
// fire on a programmatic write, notified. libadwaita has exactly one notify
|
|
15
|
+
// path (`slider_notify_active_cb`, C:66-77) and it cannot see the origin.
|
|
16
|
+
// - clicking the ROW toggles. `adw_switch_row_init` points the
|
|
17
|
+
// activatable-widget at the slider (C:160-162), so the title is part of the
|
|
18
|
+
// control; here only the handle itself worked.
|
|
19
|
+
//
|
|
20
|
+
// The TOGGLE itself is `<adw-switch>`. It used to be built here by hand —
|
|
21
|
+
// `label.adw-switch > input[type=checkbox] + span.adw-switch-slider` — and
|
|
22
|
+
// `<adw-expander-row>` built the identical thing, over a stylesheet block that
|
|
23
|
+
// was a character-for-character copy of this row's.
|
|
24
|
+
//
|
|
4
25
|
// Adapted from Adwaita Web UI Framework (https://github.com/mclellac/adwaita-web).
|
|
5
26
|
// Copyright (c) 2025 csm. MIT License.
|
|
6
27
|
// Modifications: Reimplemented as Web Component for @gjsify/adwaita-web;
|
|
7
|
-
// title/subtitle text column added to match Adw.SwitchRow
|
|
28
|
+
// title/subtitle text column added to match Adw.SwitchRow; the active state
|
|
29
|
+
// composed from @gjsify/adwaita-core; the toggle markup from <adw-switch>.
|
|
30
|
+
|
|
31
|
+
import { SwitchRowState, deriveRowLabels } from '@gjsify/adwaita-core';
|
|
32
|
+
|
|
33
|
+
// SIDE-EFFECT import, deliberately separate from the type import below: it is
|
|
34
|
+
// what guarantees `adw-switch` is defined before this module's own
|
|
35
|
+
// `customElements.define` can upgrade a server-rendered `<adw-switch-row>` and
|
|
36
|
+
// build one. A combined `import { AdwSwitch }` would NOT do it — the binding is
|
|
37
|
+
// only ever used in type position here, and this package compiles without
|
|
38
|
+
// `verbatimModuleSyntax`, so TypeScript would elide the whole statement and take
|
|
39
|
+
// the registration with it.
|
|
40
|
+
import './adw-switch.js';
|
|
41
|
+
import type { AdwSwitch } from './adw-switch.js';
|
|
8
42
|
|
|
9
43
|
export class AdwSwitchRow extends HTMLElement {
|
|
10
|
-
private
|
|
44
|
+
private _switchEl!: AdwSwitch;
|
|
11
45
|
private _titleEl!: HTMLSpanElement;
|
|
12
46
|
private _subtitleEl!: HTMLSpanElement;
|
|
47
|
+
/** The headless active flag + its notify rule (ADR 0004). */
|
|
48
|
+
private readonly _state = new SwitchRowState();
|
|
13
49
|
private _initialized = false;
|
|
14
50
|
|
|
15
51
|
static get observedAttributes() {
|
|
@@ -17,12 +53,13 @@ export class AdwSwitchRow extends HTMLElement {
|
|
|
17
53
|
}
|
|
18
54
|
|
|
19
55
|
get active(): boolean {
|
|
20
|
-
return this.hasAttribute('active');
|
|
56
|
+
return this._initialized ? this._state.active : this.hasAttribute('active');
|
|
21
57
|
}
|
|
22
58
|
|
|
23
59
|
set active(value: boolean) {
|
|
24
|
-
|
|
25
|
-
|
|
60
|
+
// Routed through the attribute so the declarative and imperative faces
|
|
61
|
+
// stay one code path; `attributeChangedCallback` drives the state.
|
|
62
|
+
this.toggleAttribute('active', !!value);
|
|
26
63
|
}
|
|
27
64
|
|
|
28
65
|
connectedCallback() {
|
|
@@ -37,42 +74,75 @@ export class AdwSwitchRow extends HTMLElement {
|
|
|
37
74
|
this._subtitleEl.className = 'adw-row-subtitle';
|
|
38
75
|
text.append(this._titleEl, this._subtitleEl);
|
|
39
76
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
this.
|
|
50
|
-
|
|
51
|
-
this.
|
|
52
|
-
|
|
53
|
-
this
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
77
|
+
this._switchEl = document.createElement('adw-switch') as AdwSwitch;
|
|
78
|
+
|
|
79
|
+
this.replaceChildren(text, this._switchEl);
|
|
80
|
+
|
|
81
|
+
this._state.setActive(this.hasAttribute('active'));
|
|
82
|
+
this._switchEl.active = this._state.active;
|
|
83
|
+
// `gtk_widget_class_set_accessible_role (…, GTK_ACCESSIBLE_ROLE_SWITCH)`
|
|
84
|
+
// plus the initial CHECKED state (adw-switch-row.c:147, :155-158).
|
|
85
|
+
this.setAttribute('role', 'switch');
|
|
86
|
+
this.setAttribute('aria-checked', String(this._state.active));
|
|
87
|
+
|
|
88
|
+
this._switchEl.addEventListener('notify::active', (event) => {
|
|
89
|
+
// The ROW is the public event surface — libadwaita has exactly one
|
|
90
|
+
// notify path for this property (`slider_notify_active_cb`, C:66-77).
|
|
91
|
+
// Letting the composed switch's own identically-named event escape
|
|
92
|
+
// would make every listener on the row see each toggle twice.
|
|
93
|
+
event.stopPropagation();
|
|
94
|
+
this._apply(this._state.setActive(this._switchEl.active));
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// Activating the row inverts the state (adw-switch-row.c:23-27). A click
|
|
98
|
+
// that landed on the switch itself is already handled by the switch —
|
|
99
|
+
// toggling again here would immediately undo it.
|
|
100
|
+
this.addEventListener('click', (event) => {
|
|
101
|
+
if (event.target instanceof Node && this._switchEl.contains(event.target)) return;
|
|
102
|
+
this._apply(this._state.activate());
|
|
60
103
|
});
|
|
61
104
|
|
|
62
105
|
this._renderText();
|
|
63
106
|
}
|
|
64
107
|
|
|
65
|
-
attributeChangedCallback(name: string
|
|
108
|
+
attributeChangedCallback(name: string) {
|
|
66
109
|
if (!this._initialized) return;
|
|
67
|
-
if (name === 'active') this.
|
|
110
|
+
if (name === 'active') this._apply(this._state.setActive(this.hasAttribute('active')));
|
|
68
111
|
else this._renderText();
|
|
69
112
|
}
|
|
70
113
|
|
|
114
|
+
/**
|
|
115
|
+
* Push a state transition out to the DOM and emit `notify::active` — but
|
|
116
|
+
* only when something actually changed, which is the `g_object_notify`
|
|
117
|
+
* gate libadwaita puts on the same transition (C:224-225 → :76).
|
|
118
|
+
*/
|
|
119
|
+
private _apply(changed: boolean) {
|
|
120
|
+
if (!changed) return;
|
|
121
|
+
// Idempotent on the switch's side too, so this write does not bounce
|
|
122
|
+
// back through the `notify::active` listener as a second transition.
|
|
123
|
+
this._switchEl.active = this._state.active;
|
|
124
|
+
this.toggleAttribute('active', this._state.active);
|
|
125
|
+
// The a11y state libadwaita updates alongside the notify (C:71-74).
|
|
126
|
+
this.setAttribute('aria-checked', String(this._state.active));
|
|
127
|
+
this.dispatchEvent(
|
|
128
|
+
new CustomEvent('notify::active', {
|
|
129
|
+
bubbles: true,
|
|
130
|
+
detail: { active: this._state.active },
|
|
131
|
+
}),
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
71
135
|
private _renderText() {
|
|
72
|
-
|
|
73
|
-
|
|
136
|
+
const { title, titleVisible, subtitle, subtitleVisible } = deriveRowLabels({
|
|
137
|
+
title: this.getAttribute('title'),
|
|
138
|
+
subtitle: this.getAttribute('subtitle'),
|
|
139
|
+
});
|
|
140
|
+
this._titleEl.textContent = title;
|
|
141
|
+
// `string_is_not_empty` applies to the TITLE too (adw-action-row.ui:49-53,
|
|
142
|
+
// inherited — AdwSwitchRow derives from AdwActionRow, adw-switch-row.c:50).
|
|
143
|
+
this._titleEl.hidden = !titleVisible;
|
|
74
144
|
this._subtitleEl.textContent = subtitle;
|
|
75
|
-
this._subtitleEl.hidden =
|
|
145
|
+
this._subtitleEl.hidden = !subtitleVisible;
|
|
76
146
|
}
|
|
77
147
|
}
|
|
78
148
|
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
// <adw-switch> — the Adwaita toggle, as an element. A 44×24 track with a hidden
|
|
2
|
+
// checkbox behind it and a knob that slides on `active`.
|
|
3
|
+
//
|
|
4
|
+
// WHY IT EXISTS: `<adw-switch-row>` and `<adw-expander-row>` each built
|
|
5
|
+
// `label.adw-switch > input[type=checkbox] + span.adw-switch-slider` by hand, and
|
|
6
|
+
// `_expander_row.scss:73-121` was a character-for-character copy of
|
|
7
|
+
// `_switch_row.scss:15-63`. The cause was structural — `_switch_row.scss:9`
|
|
8
|
+
// opened `adw-switch-row {` and nested everything inside it, so `.adw-switch` did
|
|
9
|
+
// not exist outside a switch row and the expander could not reuse it. The styles
|
|
10
|
+
// are now the unscoped `scss/_switch.scss` and the markup is here.
|
|
11
|
+
//
|
|
12
|
+
// NO CORE STATE MACHINE. The state is one boolean with no derivation, no
|
|
13
|
+
// ordering and no notify subtlety beyond "on change" — ADR 0004 is explicit that
|
|
14
|
+
// a widget with genuinely trivial behaviour does not get a core class, and the
|
|
15
|
+
// `active` ATTRIBUTE is the state (`toggleAttribute` is idempotent by
|
|
16
|
+
// definition, so "notify only on a real change" needs no guard of its own).
|
|
17
|
+
// `GtkSwitch` does carry a two-phase `active`/`state` pair for async toggles,
|
|
18
|
+
// but `refs/gtk` is EMPTY in this tree so that is unverifiable here, and neither
|
|
19
|
+
// this port nor `@gjsify/adwaita-nativescript` models it — see the ROW's state
|
|
20
|
+
// (`SwitchRowState`), which is where the notify rule that IS derived from C
|
|
21
|
+
// lives.
|
|
22
|
+
//
|
|
23
|
+
// Attributes:
|
|
24
|
+
// active — boolean; reflects (and drives) the toggled state.
|
|
25
|
+
// disabled — boolean; the control does not respond and is dimmed.
|
|
26
|
+
// Properties:
|
|
27
|
+
// active — the toggled state (get/set).
|
|
28
|
+
// disabled — whether the control is inert (get/set).
|
|
29
|
+
// Events:
|
|
30
|
+
// `notify::active` (CustomEvent, bubbles, detail = { active }) — mirrors the
|
|
31
|
+
// GObject property-notify; fires on every change, programmatic included.
|
|
32
|
+
// A widget that COMPOSES this one and publishes its own `notify::active`
|
|
33
|
+
// (`<adw-switch-row>`) stops this one at the switch, so the row stays the
|
|
34
|
+
// single public event surface.
|
|
35
|
+
//
|
|
36
|
+
// A11Y: the checkbox is left bare — no `role="switch"` — because the widgets
|
|
37
|
+
// that host one claim that role themselves (`adw_switch_row_init` sets
|
|
38
|
+
// `GTK_ACCESSIBLE_ROLE_SWITCH` on the ROW, adw-switch-row.c:147), and a nested
|
|
39
|
+
// second switch role would be announced twice. This is exactly the markup that
|
|
40
|
+
// shipped inside the two rows before the extraction.
|
|
41
|
+
//
|
|
42
|
+
// Reference: refs/adwaita-web/adwaita-web/scss/_switch.scss
|
|
43
|
+
// Reference: refs/libadwaita/src/stylesheet/widgets/_switch.scss
|
|
44
|
+
// Copyright (c) GNOME contributors (libadwaita). LGPLv2.1+.
|
|
45
|
+
// Copyright (c) 2025 csm (adwaita-web). MIT License.
|
|
46
|
+
// Modifications: Implemented as a Web Component for @gjsify/adwaita-web.
|
|
47
|
+
|
|
48
|
+
export class AdwSwitch extends HTMLElement {
|
|
49
|
+
private _input!: HTMLInputElement;
|
|
50
|
+
private _slider!: HTMLSpanElement;
|
|
51
|
+
private _initialized = false;
|
|
52
|
+
|
|
53
|
+
static get observedAttributes() {
|
|
54
|
+
return ['active', 'disabled'];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Whether the switch is on. */
|
|
58
|
+
get active(): boolean {
|
|
59
|
+
return this.hasAttribute('active');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
set active(value: boolean) {
|
|
63
|
+
// `toggleAttribute` with a force flag is a no-op when the attribute is
|
|
64
|
+
// already in the wanted state, so re-setting the current value mutates
|
|
65
|
+
// nothing and `attributeChangedCallback` — and therefore the notify —
|
|
66
|
+
// never runs. That idempotence is what a state class would otherwise be
|
|
67
|
+
// holding.
|
|
68
|
+
this.toggleAttribute('active', !!value);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Whether the switch is inert. */
|
|
72
|
+
get disabled(): boolean {
|
|
73
|
+
return this.hasAttribute('disabled');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
set disabled(value: boolean) {
|
|
77
|
+
this.toggleAttribute('disabled', !!value);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
connectedCallback() {
|
|
81
|
+
if (this._initialized) return;
|
|
82
|
+
this._initialized = true;
|
|
83
|
+
|
|
84
|
+
this.classList.add('adw-switch');
|
|
85
|
+
|
|
86
|
+
this._input = document.createElement('input');
|
|
87
|
+
this._input.type = 'checkbox';
|
|
88
|
+
this._slider = document.createElement('span');
|
|
89
|
+
this._slider.className = 'adw-switch-slider';
|
|
90
|
+
this.replaceChildren(this._input, this._slider);
|
|
91
|
+
|
|
92
|
+
// Keyboard (Space on the focused checkbox) and a direct programmatic
|
|
93
|
+
// `input.click()` both arrive here; the attribute is the state, so this
|
|
94
|
+
// just writes it and lets attributeChangedCallback do the rest.
|
|
95
|
+
this._input.addEventListener('change', () => {
|
|
96
|
+
this.active = this._input.checked;
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// The slider covers the whole track (`inset: 0`), so every pointer click
|
|
100
|
+
// lands on it rather than on the 0×0 checkbox — this is the toggle the
|
|
101
|
+
// wrapping `<label>` used to provide for free. A click that DID reach the
|
|
102
|
+
// checkbox is already handled by the listener above; toggling again here
|
|
103
|
+
// would immediately undo it.
|
|
104
|
+
this.addEventListener('click', (event) => {
|
|
105
|
+
if (this.disabled) return;
|
|
106
|
+
if (event.target === this._input) return;
|
|
107
|
+
this.active = !this.active;
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
this._render();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
attributeChangedCallback(name: string) {
|
|
114
|
+
if (!this._initialized) return;
|
|
115
|
+
this._render();
|
|
116
|
+
if (name === 'active') {
|
|
117
|
+
this.dispatchEvent(new CustomEvent('notify::active', { bubbles: true, detail: { active: this.active } }));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
private _render(): void {
|
|
122
|
+
const disabled = this.disabled;
|
|
123
|
+
this._input.checked = this.active;
|
|
124
|
+
this._input.disabled = disabled;
|
|
125
|
+
this.classList.toggle('disabled', disabled);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
customElements.define('adw-switch', AdwSwitch);
|