@gjsify/adwaita-web 0.1.7 → 0.1.9

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.
@@ -0,0 +1,9 @@
1
+ class AdwCard extends HTMLElement {
2
+ connectedCallback() {
3
+ this.classList.add("adw-card");
4
+ }
5
+ }
6
+ customElements.define("adw-card", AdwCard);
7
+ export {
8
+ AdwCard
9
+ };
@@ -0,0 +1,15 @@
1
+ // <adw-card> — Adwaita card container.
2
+ // A simple styled container with Adwaita card appearance (rounded corners,
3
+ // layered shadow, theme-aware background).
4
+ // Reference: refs/libadwaita/src/stylesheet/widgets/_misc.scss
5
+ // Reference: refs/adwaita-web/adwaita-web/scss/_card.scss
6
+ // Copyright (c) GNOME contributors (libadwaita). LGPLv2.1+.
7
+ // Modifications: Implemented as Web Component for @gjsify/adwaita-web.
8
+
9
+ export class AdwCard extends HTMLElement {
10
+ connectedCallback() {
11
+ this.classList.add('adw-card');
12
+ }
13
+ }
14
+
15
+ customElements.define('adw-card', AdwCard);
@@ -0,0 +1,63 @@
1
+ class AdwComboRow extends HTMLElement {
2
+ _select;
3
+ _valueEl;
4
+ _items = [];
5
+ _initialized = false;
6
+ static get observedAttributes() {
7
+ return ["selected"];
8
+ }
9
+ get selected() {
10
+ return this._select ? this._select.selectedIndex : parseInt(this.getAttribute("selected") || "0", 10);
11
+ }
12
+ set selected(value) {
13
+ this.setAttribute("selected", String(value));
14
+ }
15
+ connectedCallback() {
16
+ if (this._initialized) return;
17
+ this._initialized = true;
18
+ const title = this.getAttribute("title") || "";
19
+ this._items = JSON.parse(this.getAttribute("items") || "[]");
20
+ const selectedIdx = parseInt(this.getAttribute("selected") || "0", 10);
21
+ const titleEl = document.createElement("span");
22
+ titleEl.className = "adw-row-title";
23
+ titleEl.textContent = title;
24
+ const valueEl = document.createElement("span");
25
+ valueEl.className = "adw-row-value";
26
+ valueEl.textContent = this._items[selectedIdx] ?? "";
27
+ this._valueEl = valueEl;
28
+ const select = document.createElement("select");
29
+ this._items.forEach((item, i) => {
30
+ const option = document.createElement("option");
31
+ option.value = String(i);
32
+ option.textContent = item;
33
+ if (i === selectedIdx) option.selected = true;
34
+ select.appendChild(option);
35
+ });
36
+ this.appendChild(titleEl);
37
+ this.appendChild(valueEl);
38
+ this.appendChild(select);
39
+ this._select = select;
40
+ this._select.addEventListener("change", () => {
41
+ const idx = this._select.selectedIndex;
42
+ this._valueEl.textContent = this._items[idx] ?? "";
43
+ this.setAttribute("selected", String(idx));
44
+ this.dispatchEvent(new CustomEvent("notify::selected", {
45
+ bubbles: true,
46
+ detail: { selected: idx }
47
+ }));
48
+ });
49
+ }
50
+ attributeChangedCallback(name, _old, value) {
51
+ if (name === "selected" && this._select) {
52
+ const idx = parseInt(value || "0", 10);
53
+ this._select.selectedIndex = idx;
54
+ if (this._valueEl) {
55
+ this._valueEl.textContent = this._items[idx] ?? "";
56
+ }
57
+ }
58
+ }
59
+ }
60
+ customElements.define("adw-combo-row", AdwComboRow);
61
+ export {
62
+ AdwComboRow
63
+ };
@@ -0,0 +1,37 @@
1
+ class AdwHeaderBar extends HTMLElement {
2
+ _initialized = false;
3
+ _startEl = null;
4
+ _endEl = null;
5
+ /** The start (left) section container — append buttons/widgets here. */
6
+ get startSection() {
7
+ return this._startEl;
8
+ }
9
+ /** The end (right) section container — append buttons/widgets here. */
10
+ get endSection() {
11
+ return this._endEl;
12
+ }
13
+ connectedCallback() {
14
+ if (this._initialized) return;
15
+ this._initialized = true;
16
+ const title = this.getAttribute("title") || "";
17
+ const startChildren = Array.from(this.querySelectorAll(':scope > [slot="start"]'));
18
+ const endChildren = Array.from(this.querySelectorAll(':scope > [slot="end"]'));
19
+ this._startEl = document.createElement("div");
20
+ this._startEl.className = "adw-header-bar-start";
21
+ for (const child of startChildren) this._startEl.appendChild(child);
22
+ const center = document.createElement("div");
23
+ center.className = "adw-header-bar-center";
24
+ const titleEl = document.createElement("span");
25
+ titleEl.className = "adw-header-bar-title";
26
+ titleEl.textContent = title;
27
+ center.appendChild(titleEl);
28
+ this._endEl = document.createElement("div");
29
+ this._endEl.className = "adw-header-bar-end";
30
+ for (const child of endChildren) this._endEl.appendChild(child);
31
+ this.replaceChildren(this._startEl, center, this._endEl);
32
+ }
33
+ }
34
+ customElements.define("adw-header-bar", AdwHeaderBar);
35
+ export {
36
+ AdwHeaderBar
37
+ };
@@ -0,0 +1,101 @@
1
+ class AdwOverlaySplitView extends HTMLElement {
2
+ _initialized = false;
3
+ _sidebarEl;
4
+ _contentEl;
5
+ _backdropEl;
6
+ static get observedAttributes() {
7
+ return ["show-sidebar", "collapsed", "sidebar-position", "min-sidebar-width", "max-sidebar-width", "sidebar-width-fraction"];
8
+ }
9
+ get showSidebar() {
10
+ return this.hasAttribute("show-sidebar");
11
+ }
12
+ set showSidebar(v) {
13
+ if (v) this.setAttribute("show-sidebar", "");
14
+ else this.removeAttribute("show-sidebar");
15
+ }
16
+ get collapsed() {
17
+ return this.hasAttribute("collapsed");
18
+ }
19
+ set collapsed(v) {
20
+ if (v) this.setAttribute("collapsed", "");
21
+ else this.removeAttribute("collapsed");
22
+ }
23
+ get minSidebarWidth() {
24
+ return parseFloat(this.getAttribute("min-sidebar-width") || "280");
25
+ }
26
+ get maxSidebarWidth() {
27
+ return parseFloat(this.getAttribute("max-sidebar-width") || "400");
28
+ }
29
+ get sidebarWidthFraction() {
30
+ return parseFloat(this.getAttribute("sidebar-width-fraction") || "0.30");
31
+ }
32
+ connectedCallback() {
33
+ if (this._initialized) return;
34
+ this._initialized = true;
35
+ const sidebarChildren = Array.from(this.querySelectorAll('[slot="sidebar"]'));
36
+ const contentChildren = Array.from(this.querySelectorAll('[slot="content"]'));
37
+ const unslotted = Array.from(this.childNodes).filter(
38
+ (n) => !sidebarChildren.includes(n) && !contentChildren.includes(n)
39
+ );
40
+ this.replaceChildren();
41
+ this._sidebarEl = document.createElement("div");
42
+ this._sidebarEl.className = "adw-osv-sidebar";
43
+ sidebarChildren.forEach((c) => this._sidebarEl.appendChild(c));
44
+ this._contentEl = document.createElement("div");
45
+ this._contentEl.className = "adw-osv-content";
46
+ contentChildren.forEach((c) => this._contentEl.appendChild(c));
47
+ unslotted.forEach((c) => this._contentEl.appendChild(c));
48
+ this._backdropEl = document.createElement("div");
49
+ this._backdropEl.className = "adw-osv-backdrop";
50
+ this._backdropEl.addEventListener("click", () => this.hideSidebar());
51
+ this.append(this._sidebarEl, this._contentEl, this._backdropEl);
52
+ this._syncClasses();
53
+ this._syncSidebarWidth();
54
+ }
55
+ attributeChangedCallback(_name, _old, _val) {
56
+ if (!this._initialized) return;
57
+ this._syncClasses();
58
+ if (_name === "min-sidebar-width" || _name === "max-sidebar-width" || _name === "sidebar-width-fraction") {
59
+ this._syncSidebarWidth();
60
+ }
61
+ }
62
+ openSidebar() {
63
+ this.showSidebar = true;
64
+ this.dispatchEvent(new CustomEvent("sidebar-toggled", { detail: { isVisible: true } }));
65
+ }
66
+ hideSidebar() {
67
+ this.showSidebar = false;
68
+ this.dispatchEvent(new CustomEvent("sidebar-toggled", { detail: { isVisible: false } }));
69
+ }
70
+ toggleSidebar() {
71
+ this.showSidebar = !this.showSidebar;
72
+ this.dispatchEvent(new CustomEvent("sidebar-toggled", {
73
+ detail: { isVisible: this.showSidebar }
74
+ }));
75
+ }
76
+ _syncClasses() {
77
+ this.classList.toggle("collapsed", this.collapsed);
78
+ this.classList.toggle("show-sidebar", this.showSidebar);
79
+ const pos = this.getAttribute("sidebar-position") || "start";
80
+ this.classList.toggle("sidebar-start", pos === "start");
81
+ this.classList.toggle("sidebar-end", pos === "end");
82
+ if (this._sidebarEl && !this.collapsed) {
83
+ if (!this.showSidebar) {
84
+ const w = this._sidebarEl.offsetWidth;
85
+ this._sidebarEl.style.marginRight = `-${w}px`;
86
+ } else {
87
+ this._sidebarEl.style.marginRight = "";
88
+ }
89
+ }
90
+ }
91
+ _syncSidebarWidth() {
92
+ if (!this._sidebarEl) return;
93
+ this._sidebarEl.style.minWidth = `${this.minSidebarWidth}px`;
94
+ this._sidebarEl.style.maxWidth = `${this.maxSidebarWidth}px`;
95
+ this._sidebarEl.style.width = `${this.sidebarWidthFraction * 100}%`;
96
+ }
97
+ }
98
+ customElements.define("adw-overlay-split-view", AdwOverlaySplitView);
99
+ export {
100
+ AdwOverlaySplitView
101
+ };
@@ -0,0 +1,24 @@
1
+ class AdwPreferencesGroup extends HTMLElement {
2
+ _initialized = false;
3
+ connectedCallback() {
4
+ if (this._initialized) return;
5
+ this._initialized = true;
6
+ const title = this.getAttribute("title") || "";
7
+ const children = Array.from(this.childNodes);
8
+ const header = document.createElement("div");
9
+ header.className = "adw-preferences-group-header";
10
+ const titleEl = document.createElement("span");
11
+ titleEl.className = "adw-preferences-group-title";
12
+ titleEl.textContent = title;
13
+ header.appendChild(titleEl);
14
+ const listbox = document.createElement("div");
15
+ listbox.className = "adw-preferences-group-listbox";
16
+ children.forEach((child) => listbox.appendChild(child));
17
+ this.appendChild(header);
18
+ this.appendChild(listbox);
19
+ }
20
+ }
21
+ customElements.define("adw-preferences-group", AdwPreferencesGroup);
22
+ export {
23
+ AdwPreferencesGroup
24
+ };
@@ -0,0 +1,104 @@
1
+ class AdwSpinRow extends HTMLElement {
2
+ _input;
3
+ _min = 0;
4
+ _max = 100;
5
+ _step = 1;
6
+ _value = 0;
7
+ _initialized = false;
8
+ static get observedAttributes() {
9
+ return ["value", "min", "max", "step"];
10
+ }
11
+ get value() {
12
+ return this._value;
13
+ }
14
+ set value(v) {
15
+ const clamped = Math.min(this._max, Math.max(this._min, v));
16
+ const decimals = this._countDecimals(this._step);
17
+ this._value = parseFloat(clamped.toFixed(decimals));
18
+ if (this._input) {
19
+ this._input.value = this._formatValue(this._value);
20
+ }
21
+ this.setAttribute("value", String(this._value));
22
+ }
23
+ connectedCallback() {
24
+ if (this._initialized) return;
25
+ this._initialized = true;
26
+ const title = this.getAttribute("title") || "";
27
+ this._min = parseFloat(this.getAttribute("min") || "0");
28
+ this._max = parseFloat(this.getAttribute("max") || "100");
29
+ this._step = parseFloat(this.getAttribute("step") || "1");
30
+ this._value = parseFloat(this.getAttribute("value") || String(this._min));
31
+ const titleEl = document.createElement("span");
32
+ titleEl.className = "adw-row-title";
33
+ titleEl.textContent = title;
34
+ const control = document.createElement("div");
35
+ control.className = "adw-spin-control";
36
+ const decBtn = document.createElement("button");
37
+ decBtn.className = "adw-spin-dec";
38
+ decBtn.textContent = "\u2212";
39
+ decBtn.addEventListener("click", () => this._adjust(-this._step));
40
+ const input = document.createElement("input");
41
+ input.type = "text";
42
+ input.value = this._formatValue(this._value);
43
+ input.addEventListener("change", () => {
44
+ const parsed = parseFloat(input.value);
45
+ if (!isNaN(parsed)) {
46
+ this.value = parsed;
47
+ this._emitChange();
48
+ } else {
49
+ input.value = this._formatValue(this._value);
50
+ }
51
+ });
52
+ const incBtn = document.createElement("button");
53
+ incBtn.className = "adw-spin-inc";
54
+ incBtn.textContent = "+";
55
+ incBtn.addEventListener("click", () => this._adjust(this._step));
56
+ control.append(decBtn, input, incBtn);
57
+ this.append(titleEl, control);
58
+ this._input = input;
59
+ }
60
+ attributeChangedCallback(name, _old, val) {
61
+ if (!this._initialized) return;
62
+ const num = parseFloat(val || "0");
63
+ switch (name) {
64
+ case "value":
65
+ if (num !== this._value) {
66
+ this._value = Math.min(this._max, Math.max(this._min, num));
67
+ this._input.value = this._formatValue(this._value);
68
+ }
69
+ break;
70
+ case "min":
71
+ this._min = num;
72
+ break;
73
+ case "max":
74
+ this._max = num;
75
+ break;
76
+ case "step":
77
+ this._step = num;
78
+ break;
79
+ }
80
+ }
81
+ _adjust(delta) {
82
+ this.value = this._value + delta;
83
+ this._emitChange();
84
+ }
85
+ _emitChange() {
86
+ this.dispatchEvent(new CustomEvent("notify::value", {
87
+ bubbles: true,
88
+ detail: { value: this._value }
89
+ }));
90
+ }
91
+ _countDecimals(n) {
92
+ const s = String(n);
93
+ const dot = s.indexOf(".");
94
+ return dot === -1 ? 0 : s.length - dot - 1;
95
+ }
96
+ _formatValue(v) {
97
+ const decimals = this._countDecimals(this._step);
98
+ return decimals > 0 ? v.toFixed(decimals) : String(v);
99
+ }
100
+ }
101
+ customElements.define("adw-spin-row", AdwSpinRow);
102
+ export {
103
+ AdwSpinRow
104
+ };
@@ -0,0 +1,51 @@
1
+ class AdwSwitchRow extends HTMLElement {
2
+ _checkbox;
3
+ _initialized = false;
4
+ static get observedAttributes() {
5
+ return ["active"];
6
+ }
7
+ get active() {
8
+ return this.hasAttribute("active");
9
+ }
10
+ set active(value) {
11
+ if (value) this.setAttribute("active", "");
12
+ else this.removeAttribute("active");
13
+ }
14
+ connectedCallback() {
15
+ if (this._initialized) return;
16
+ this._initialized = true;
17
+ const title = this.getAttribute("title") || "";
18
+ const checked = this.hasAttribute("active");
19
+ const titleEl = document.createElement("span");
20
+ titleEl.className = "adw-row-title";
21
+ titleEl.textContent = title;
22
+ const label = document.createElement("label");
23
+ label.className = "adw-switch";
24
+ const input = document.createElement("input");
25
+ input.type = "checkbox";
26
+ input.checked = checked;
27
+ const slider = document.createElement("span");
28
+ slider.className = "adw-switch-slider";
29
+ label.appendChild(input);
30
+ label.appendChild(slider);
31
+ this.appendChild(titleEl);
32
+ this.appendChild(label);
33
+ this._checkbox = input;
34
+ this._checkbox.addEventListener("change", () => {
35
+ this.toggleAttribute("active", this._checkbox.checked);
36
+ this.dispatchEvent(new CustomEvent("notify::active", {
37
+ bubbles: true,
38
+ detail: { active: this._checkbox.checked }
39
+ }));
40
+ });
41
+ }
42
+ attributeChangedCallback(name, _old, value) {
43
+ if (name === "active" && this._checkbox) {
44
+ this._checkbox.checked = value !== null;
45
+ }
46
+ }
47
+ }
48
+ customElements.define("adw-switch-row", AdwSwitchRow);
49
+ export {
50
+ AdwSwitchRow
51
+ };
@@ -0,0 +1,31 @@
1
+ class AdwToastOverlay extends HTMLElement {
2
+ /**
3
+ * Show a toast notification.
4
+ * @param title - The text to display.
5
+ * @param timeout - Time in ms before the toast auto-dismisses (default 2000).
6
+ */
7
+ addToast(title, timeout = 2e3) {
8
+ const toast = document.createElement("div");
9
+ toast.className = "adw-toast";
10
+ const titleEl = document.createElement("span");
11
+ titleEl.className = "adw-toast-title";
12
+ titleEl.textContent = title;
13
+ toast.appendChild(titleEl);
14
+ this.appendChild(toast);
15
+ requestAnimationFrame(() => {
16
+ toast.classList.add("visible");
17
+ });
18
+ setTimeout(() => {
19
+ toast.classList.remove("visible");
20
+ toast.classList.add("hiding");
21
+ toast.addEventListener("transitionend", () => toast.remove(), { once: true });
22
+ setTimeout(() => {
23
+ if (toast.parentNode) toast.remove();
24
+ }, 300);
25
+ }, timeout);
26
+ }
27
+ }
28
+ customElements.define("adw-toast-overlay", AdwToastOverlay);
29
+ export {
30
+ AdwToastOverlay
31
+ };
@@ -0,0 +1,12 @@
1
+ class AdwWindow extends HTMLElement {
2
+ connectedCallback() {
3
+ const w = this.getAttribute("width");
4
+ const h = this.getAttribute("height");
5
+ if (w) this.style.width = `${w}px`;
6
+ if (h) this.style.height = `${h}px`;
7
+ }
8
+ }
9
+ customElements.define("adw-window", AdwWindow);
10
+ export {
11
+ AdwWindow
12
+ };
package/src/index.js ADDED
@@ -0,0 +1,21 @@
1
+ import "@gjsify/adwaita-fonts";
2
+ import { AdwCard } from "./elements/adw-card.js";
3
+ import { AdwWindow } from "./elements/adw-window.js";
4
+ import { AdwHeaderBar } from "./elements/adw-header-bar.js";
5
+ import { AdwPreferencesGroup } from "./elements/adw-preferences-group.js";
6
+ import { AdwSwitchRow } from "./elements/adw-switch-row.js";
7
+ import { AdwComboRow } from "./elements/adw-combo-row.js";
8
+ import { AdwSpinRow } from "./elements/adw-spin-row.js";
9
+ import { AdwToastOverlay } from "./elements/adw-toast-overlay.js";
10
+ import { AdwOverlaySplitView } from "./elements/adw-overlay-split-view.js";
11
+ export {
12
+ AdwCard,
13
+ AdwComboRow,
14
+ AdwHeaderBar,
15
+ AdwOverlaySplitView,
16
+ AdwPreferencesGroup,
17
+ AdwSpinRow,
18
+ AdwSwitchRow,
19
+ AdwToastOverlay,
20
+ AdwWindow
21
+ };
package/src/index.ts CHANGED
@@ -1,11 +1,13 @@
1
1
  // @gjsify/adwaita-web — Adwaita/Libadwaita web components for browser targets.
2
- // Importing this module registers all custom elements and injects the Adwaita CSS.
2
+ // Importing this module registers all custom elements. The accompanying
3
+ // stylesheet must be imported separately as `@gjsify/adwaita-web/style.css`
4
+ // (or via SCSS partials at `@gjsify/adwaita-web/scss/...`).
3
5
  // Reference: refs/libadwaita (colors/sizing), refs/adwaita-web (component patterns).
4
6
 
5
7
  import '@gjsify/adwaita-fonts'; // Registers @font-face (fontsource pattern)
6
- import { adwaitaCSS } from './adwaita-css.js';
7
8
 
8
9
  // Register custom elements (side-effect imports)
10
+ export { AdwCard } from './elements/adw-card.js';
9
11
  export { AdwWindow } from './elements/adw-window.js';
10
12
  export { AdwHeaderBar } from './elements/adw-header-bar.js';
11
13
  export { AdwPreferencesGroup } from './elements/adw-preferences-group.js';
@@ -14,10 +16,3 @@ export { AdwComboRow } from './elements/adw-combo-row.js';
14
16
  export { AdwSpinRow } from './elements/adw-spin-row.js';
15
17
  export { AdwToastOverlay } from './elements/adw-toast-overlay.js';
16
18
  export { AdwOverlaySplitView } from './elements/adw-overlay-split-view.js';
17
-
18
- // Inject Adwaita CSS into the document
19
- if (typeof document !== 'undefined') {
20
- const style = document.createElement('style');
21
- style.textContent = adwaitaCSS;
22
- document.head.appendChild(style);
23
- }
package/style.css.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ // Type declaration for the @gjsify/adwaita-web/style.css side-effect import.
2
+ // The actual file is the compiled stylesheet at dist/adwaita-web.css
3
+ // (generated by `yarn build:scss`).
4
+ export {};
@@ -1 +0,0 @@
1
- export declare const adwaitaCSS: string;
package/tsconfig.json DELETED
@@ -1,16 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "module": "ESNext",
4
- "target": "ESNext",
5
- "lib": ["ESNext", "DOM"],
6
- "rootDir": "src",
7
- "outDir": "lib",
8
- "declarationDir": "lib/types",
9
- "declaration": true,
10
- "moduleResolution": "bundler",
11
- "allowImportingTsExtensions": true,
12
- "emitDeclarationOnly": true,
13
- "strict": false
14
- },
15
- "include": ["src/**/*.ts"]
16
- }