diff --git a/src/UI/Components/Button.js b/src/UI/Components/Button.js
index 148b4ee3..375e1ad9 100644
--- a/src/UI/Components/Button.js
+++ b/src/UI/Components/Button.js
@@ -1,4 +1,4 @@
-import { Component } from "../../util/Component.js";
+import { Component, defineComponent } from "../../util/Component.js";
export default class Button extends Component {
static PROPERTIES = {
@@ -64,10 +64,4 @@ export default class Button extends Component {
}
}
-// TODO: This is necessary because files can be loaded from
-// both `/src/UI` and `/UI` in the URL; we need to fix that
-if ( ! window.__component_button ) {
- window.__component_button = true;
-
- customElements.define('c-button', Button);
-}
+defineComponent('c-button', Button);
diff --git a/src/UI/Components/CodeEntryView.js b/src/UI/Components/CodeEntryView.js
index ba8197ce..a4d0c538 100644
--- a/src/UI/Components/CodeEntryView.js
+++ b/src/UI/Components/CodeEntryView.js
@@ -1,4 +1,4 @@
-import { Component } from "../../util/Component.js";
+import { Component, defineComponent } from "../../util/Component.js";
export default class CodeEntryView extends Component {
static PROPERTIES = {
@@ -215,10 +215,4 @@ export default class CodeEntryView extends Component {
}
}
-// TODO: This is necessary because files can be loaded from
-// both `/src/UI` and `/UI` in the URL; we need to fix that
-if ( ! window.__component_codeEntryView ) {
- window.__component_codeEntryView = true;
-
- customElements.define('c-code-entry-view', CodeEntryView);
-}
+defineComponent('c-code-entry-view', CodeEntryView);
diff --git a/src/UI/Components/ConfirmationsView.js b/src/UI/Components/ConfirmationsView.js
index 1b662186..19ec4d3e 100644
--- a/src/UI/Components/ConfirmationsView.js
+++ b/src/UI/Components/ConfirmationsView.js
@@ -1,4 +1,4 @@
-import { Component } from "../../util/Component.js";
+import { Component, defineComponent } from "../../util/Component.js";
/**
* Display a list of checkboxes for the user to confirm.
@@ -58,10 +58,4 @@ export default class ConfirmationsView extends Component {
}
}
-// TODO: This is necessary because files can be loaded from
-// both `/src/UI` and `/UI` in the URL; we need to fix that
-if ( ! window.__component_confirmationsView ) {
- window.__component_confirmationsView = true;
-
- customElements.define('c-confirmations-view', ConfirmationsView);
-}
+defineComponent('c-confirmations-view', ConfirmationsView);
diff --git a/src/UI/Components/Flexer.js b/src/UI/Components/Flexer.js
index 50deac89..838be891 100644
--- a/src/UI/Components/Flexer.js
+++ b/src/UI/Components/Flexer.js
@@ -1,4 +1,4 @@
-import { Component } from "../../util/Component.js";
+import { Component, defineComponent } from "../../util/Component.js";
/**
* Allows a flex layout of composed components to be
@@ -38,10 +38,4 @@ export default class Flexer extends Component {
}
}
-// TODO: This is necessary because files can be loaded from
-// both `/src/UI` and `/UI` in the URL; we need to fix that
-if ( ! window.__component_flexer ) {
- window.__component_flexer = true;
-
- customElements.define('c-flexer', Flexer);
-}
+defineComponent('c-flexer', Flexer);
diff --git a/src/UI/Components/JustHTML.js b/src/UI/Components/JustHTML.js
index f3dcc157..36e17db5 100644
--- a/src/UI/Components/JustHTML.js
+++ b/src/UI/Components/JustHTML.js
@@ -1,4 +1,4 @@
-import { Component } from "../../util/Component.js";
+import { Component, defineComponent } from "../../util/Component.js";
/**
* Allows using an HTML string as a component.
@@ -15,10 +15,4 @@ export default class JustHTML extends Component {
}
}
-// TODO: This is necessary because files can be loaded from
-// both `/src/UI` and `/UI` in the URL; we need to fix that
-if ( ! window.__component_justHTML ) {
- window.__component_justHTML = true;
-
- customElements.define('c-just-html', JustHTML);
-}
+defineComponent('c-just-html', JustHTML);
diff --git a/src/UI/Components/QRCode.js b/src/UI/Components/QRCode.js
index 859a629e..bb974d99 100644
--- a/src/UI/Components/QRCode.js
+++ b/src/UI/Components/QRCode.js
@@ -1,4 +1,4 @@
-import { Component } from "../../util/Component.js";
+import { Component, defineComponent } from "../../util/Component.js";
import UIComponentWindow from "../UIComponentWindow.js";
export default class QRCodeView extends Component {
@@ -78,10 +78,4 @@ export default class QRCodeView extends Component {
}
}
-// TODO: This is necessary because files can be loaded from
-// both `/src/UI` and `/UI` in the URL; we need to fix that
-if ( ! window.__component_qr_code ) {
- window.__component_qr_code = true;
-
- customElements.define('c-qr-code', QRCodeView);
-}
+defineComponent('c-qr-code', QRCodeView);
diff --git a/src/UI/Components/RecoveryCodeEntryView.js b/src/UI/Components/RecoveryCodeEntryView.js
index adb9f2d0..b17d27e1 100644
--- a/src/UI/Components/RecoveryCodeEntryView.js
+++ b/src/UI/Components/RecoveryCodeEntryView.js
@@ -1,4 +1,4 @@
-import { Component } from "../../util/Component.js";
+import { Component, defineComponent } from "../../util/Component.js";
export default class RecoveryCodeEntryView extends Component {
static PROPERTIES = {
@@ -84,4 +84,4 @@ export default class RecoveryCodeEntryView extends Component {
}
}
-customElements.define('c-recovery-code-entry', RecoveryCodeEntryView);
\ No newline at end of file
+defineComponent('c-recovery-code-entry', RecoveryCodeEntryView);
diff --git a/src/UI/Components/RecoveryCodesView.js b/src/UI/Components/RecoveryCodesView.js
index 705a041e..e72bd37f 100644
--- a/src/UI/Components/RecoveryCodesView.js
+++ b/src/UI/Components/RecoveryCodesView.js
@@ -1,4 +1,4 @@
-import { Component } from "../../util/Component.js";
+import { Component, defineComponent } from "../../util/Component.js";
export default class RecoveryCodesView extends Component {
static PROPERTIES = {
@@ -91,10 +91,4 @@ export default class RecoveryCodesView extends Component {
}
}
-// TODO: This is necessary because files can be loaded from
-// both `/src/UI` and `/UI` in the URL; we need to fix that
-if ( ! window.__component_recoveryCodesView ) {
- window.__component_recoveryCodesView = true;
-
- customElements.define('c-recovery-codes-view', RecoveryCodesView);
-}
+defineComponent('c-recovery-codes-view', RecoveryCodesView);
diff --git a/src/UI/Components/Slider.js b/src/UI/Components/Slider.js
new file mode 100644
index 00000000..26308048
--- /dev/null
+++ b/src/UI/Components/Slider.js
@@ -0,0 +1,108 @@
+/**
+ * Copyright (C) 2024 Puter Technologies Inc.
+ *
+ * This file is part of Puter.
+ *
+ * Puter is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+import { Component, defineComponent } from "../../util/Component.js";
+
+/**
+ * Slider: A labeled slider input.
+ */
+export default class Slider extends Component {
+ static PROPERTIES = {
+ name: { value: null },
+ label: { value: null },
+ min: { value: 0 },
+ max: { value: 100 },
+ value: { value: null },
+ step: { value: 1 },
+ on_change: { value: null },
+ };
+
+ static RENDER_MODE = Component.NO_SHADOW;
+
+ static CSS = /*css*/`
+ .slider-label {
+ color: var(--primary-color);
+ }
+
+ .slider-input {
+ --webkit-appearance: none;
+ width: 100%;
+ height: 25px;
+ background: #d3d3d3;
+ outline: none;
+ opacity: 0.7;
+ --webkit-transition: .2s;
+ transition: opacity .2s;
+ }
+
+ .slider-input:hover {
+ opacity: 1;
+ }
+
+ .slider-input::-webkit-slider-thumb {
+ --webkit-appearance: none;
+ appearance: none;
+ width: 25px;
+ height: 25px;
+ background: #04AA6D;
+ cursor: pointer;
+ }
+
+ .slider-input::-moz-range-thumb {
+ width: 25px;
+ height: 25px;
+ background: #04AA6D;
+ cursor: pointer;
+ }
+ `;
+
+ create_template ({ template }) {
+ const min = this.get('min');
+ const max = this.get('max');
+ const value = this.get('value') ?? min;
+ const step = this.get('step') ?? 1;
+ const label = this.get('label') ?? this.get('name');
+
+ $(template).html(/*html*/`
+
+
+
+
+ `);
+ }
+
+ on_ready ({ listen }) {
+ const input = this.dom_.querySelector('.slider-input');
+
+ input.addEventListener('input', e => {
+ const on_change = this.get('on_change');
+ if (on_change) {
+ const name = this.get('name');
+ const label = this.get('label') ?? name;
+ e.meta = { name, label };
+ on_change(e);
+ }
+ });
+
+ listen('value', value => {
+ input.value = value;
+ });
+ }
+}
+
+defineComponent('c-slider', Slider);
diff --git a/src/UI/Components/StepHeading.js b/src/UI/Components/StepHeading.js
index 2f61f981..b095d832 100644
--- a/src/UI/Components/StepHeading.js
+++ b/src/UI/Components/StepHeading.js
@@ -1,4 +1,4 @@
-import { Component } from "../../util/Component.js";
+import { Component, defineComponent } from "../../util/Component.js";
/**
* StepHeading renders a heading with a leading symbol.
@@ -58,10 +58,4 @@ export default class StepHeading extends Component {
}
}
-// TODO: This is necessary because files can be loaded from
-// both `/src/UI` and `/UI` in the URL; we need to fix that
-if ( ! window.__component_stepHeading ) {
- window.__component_stepHeading = true;
-
- customElements.define('c-step-heading', StepHeading);
-}
+defineComponent('c-step-heading', StepHeading);
diff --git a/src/UI/Components/StepView.js b/src/UI/Components/StepView.js
index 5c3cff36..4bc98e77 100644
--- a/src/UI/Components/StepView.js
+++ b/src/UI/Components/StepView.js
@@ -1,4 +1,4 @@
-import { Component } from "../../util/Component.js";
+import { Component, defineComponent } from "../../util/Component.js";
export default class StepView extends Component {
static PROPERTIES = {
@@ -64,10 +64,4 @@ export default class StepView extends Component {
}
}
-// TODO: This is necessary because files can be loaded from
-// both `/src/UI` and `/UI` in the URL; we need to fix that
-if ( ! window.__component_stepView ) {
- window.__component_stepView = true;
-
- customElements.define('c-step-view', StepView);
-}
+defineComponent('c-step-view', StepView);
diff --git a/src/UI/Components/StringView.js b/src/UI/Components/StringView.js
index 2ad4228e..a41552c6 100644
--- a/src/UI/Components/StringView.js
+++ b/src/UI/Components/StringView.js
@@ -1,4 +1,4 @@
-import { Component } from "../../util/Component.js";
+import { Component, defineComponent } from "../../util/Component.js";
/**
* A simple component that displays a string in the
@@ -42,10 +42,4 @@ export default class StringView extends Component {
}
}
-// TODO: This is necessary because files can be loaded from
-// both `/src/UI` and `/UI` in the URL; we need to fix that
-if ( ! window.__component_stringView ) {
- window.__component_stringView = true;
-
- customElements.define('c-string-view', StringView);
-}
+defineComponent('c-string-view', StringView);
diff --git a/src/UI/Components/TestView.js b/src/UI/Components/TestView.js
index b6b7dae9..9fd76a3c 100644
--- a/src/UI/Components/TestView.js
+++ b/src/UI/Components/TestView.js
@@ -1,4 +1,4 @@
-import { Component } from "../../util/Component.js";
+import { Component, defineComponent } from "../../util/Component.js";
/**
* A simple component when you just need to test something.
@@ -19,10 +19,4 @@ export default class TestView extends Component {
}
}
-// TODO: This is necessary because files can be loaded from
-// both `/src/UI` and `/UI` in the URL; we need to fix that
-if ( ! window.__component_testView ) {
- window.__component_testView = true;
-
- customElements.define('c-test-view', TestView);
-}
+defineComponent('c-test-view', TestView);
diff --git a/src/UI/UIWindowThemeDialog.js b/src/UI/UIWindowThemeDialog.js
index d1e437d7..234bf34f 100644
--- a/src/UI/UIWindowThemeDialog.js
+++ b/src/UI/UIWindowThemeDialog.js
@@ -1,13 +1,73 @@
-import UIWindow from "./UIWindow.js";
-import UIWindowColorPicker from "./UIWindowColorPicker.js";
+import UIComponentWindow from './UIComponentWindow.js';
+import Button from './Components/Button.js';
+import Flexer from './Components/Flexer.js';
+import Slider from './Components/Slider.js';
const UIWindowThemeDialog = async function UIWindowThemeDialog (options) {
options = options ?? {};
const services = globalThis.services;
const svc_theme = services.get('theme');
- const w = await UIWindow({
+ let state = {};
+
+ const slider_ch = (e) => {
+ state[e.meta.name] = e.target.value;
+ if (e.meta.name === 'lig') {
+ state.light_text = e.target.value < 60 ? true : false;
+ }
+ svc_theme.apply(state);
+ };
+
+ const hue_slider = new Slider({
+ label: i18n('hue'),
+ name: 'hue', min: 0, max: 360,
+ value: svc_theme.get('hue'),
+ on_change: slider_ch,
+ });
+ const sat_slider = new Slider({
+ label: i18n('saturation'),
+ name: 'sat', min: 0, max: 100,
+ value: svc_theme.get('sat'),
+ on_change: slider_ch,
+ });
+ const lig_slider = new Slider({
+ label: i18n('lightness'),
+ name: 'lig', min: 0, max: 100,
+ value: svc_theme.get('lig'),
+ on_change: slider_ch,
+ });
+ const alpha_slider = new Slider({
+ label: i18n('transparency'),
+ name: 'alpha', min: 0, max: 1, step: 0.01,
+ value: svc_theme.get('alpha'),
+ on_change: slider_ch,
+ });
+
+ const component = new Flexer({
+ children: [
+ new Button({
+ label: i18n('reset_colors'),
+ style: 'secondary',
+ on_click: () => {
+ svc_theme.reset();
+ state = {};
+ hue_slider.set('value', svc_theme.get('hue'));
+ sat_slider.set('value', svc_theme.get('sat'));
+ lig_slider.set('value', svc_theme.get('lig'));
+ alpha_slider.set('value', svc_theme.get('alpha'));
+ },
+ }),
+ hue_slider,
+ sat_slider,
+ lig_slider,
+ alpha_slider,
+ ],
+ gap: '10pt',
+ });
+
+ const w = await UIComponentWindow({
title: i18n('ui_colors'),
+ component,
icon: null,
uid: null,
is_dir: false,
@@ -48,105 +108,6 @@ const UIWindowThemeDialog = async function UIWindowThemeDialog (options) {
},
...options.window_options,
});
- const w_body = w.querySelector('.window-body');
-
- const Button = ({ label }) => {
- const el = document.createElement('button');
- el.textContent = label;
- el.classList.add('button', 'button-block');
- return {
- appendTo (parent) {
- parent.appendChild(el);
- return this;
- },
- onPress (cb) {
- el.addEventListener('click', cb);
- return this;
- },
- };
- }
-
- const Slider = ({ name, label, min, max, initial, step }) => {
- label = label ?? name;
- const wrap = document.createElement('div');
- const label_el = document.createElement('label');
- label_el.textContent = label;
- label_el.style = "color:var(--primary-color)";
- wrap.appendChild(label_el);
- const el = document.createElement('input');
- wrap.appendChild(el);
- el.type = 'range';
- el.min = min;
- el.max = max;
- el.defaultValue = initial ?? min;
- el.step = step ?? 1;
- el.classList.add('theme-dialog-slider');
-
-
- return {
- appendTo (parent) {
- parent.appendChild(wrap);
- return this;
- },
- onChange (cb) {
- el.addEventListener('input', e => {
- e.meta = { name, label };
- cb(e);
- });
- return this;
- },
- };
- };
-
- const state = {};
-
- const slider_ch = (e) => {
- state[e.meta.name] = e.target.value;
- if (e.meta.name === 'lig') {
- state.light_text = e.target.value < 60 ? true : false;
- }
- svc_theme.apply(state);
- };
-
- Button({ label: i18n('reset_colors') })
- .appendTo(w_body)
- .onPress(() => {
- svc_theme.reset();
- })
- ;
-
- Slider({
- label: i18n('hue'),
- name: 'hue', min: 0, max: 360,
- initial: svc_theme.get('hue'),
- })
- .appendTo(w_body)
- .onChange(slider_ch)
- ;
- Slider({
- label: i18n('saturation'),
- name: 'sat', min: 0, max: 100,
- initial: svc_theme.get('sat'),
- })
- .appendTo(w_body)
- .onChange(slider_ch)
- ;
- Slider({
- label: i18n('lightness'),
- name: 'lig', min: 0, max: 100,
- initial: svc_theme.get('lig'),
- })
- .appendTo(w_body)
- .onChange(slider_ch)
- ;
- Slider({
- label: i18n('transparency'),
- name: 'alpha', min: 0, max: 1, step: 0.01,
- initial: svc_theme.get('alpha'),
- })
- .appendTo(w_body)
- .onChange(slider_ch)
- ;
return {};
}
diff --git a/src/css/style.css b/src/css/style.css
index 9763529f..0fe24790 100644
--- a/src/css/style.css
+++ b/src/css/style.css
@@ -3777,37 +3777,6 @@ fieldset[name=number-code] {
margin-bottom: 20px;
}
-.theme-dialog-slider {
- --webkit-appearance: none;
- width: 100%;
- height: 25px;
- background: #d3d3d3;
- outline: none;
- opacity: 0.7;
- --webkit-transition: .2s;
- transition: opacity .2s;
-}
-
-.theme-dialog-slider:hover {
- opacity: 1;
-}
-
-.theme-dialog-slider::-webkit-slider-thumb {
- --webkit-appearance: none;
- appearance: none;
- width: 25px;
- height: 25px;
- background: #04AA6D;
- cursor: pointer;
-}
-
-.theme-dialog-slider::-moz-range-thumb {
- width: 25px;
- height: 25px;
- background: #04AA6D;
- cursor: pointer;
-}
-
.session-manager-list {
display: flex;
flex-direction: column;
diff --git a/src/util/Component.js b/src/util/Component.js
index 115648ee..4439e3ce 100644
--- a/src/util/Component.js
+++ b/src/util/Component.js
@@ -136,3 +136,11 @@ export class Component extends HTMLElement {
};
}
}
+
+export const defineComponent = (name, component) => {
+ // TODO: This is necessary because files can be loaded from
+ // both `/src/UI` and `/UI` in the URL; we need to fix that
+ if ( ! customElements.get(name) ) {
+ customElements.define(name, component);
+ }
+};