Explorar o código

Merge pull request #381 from AtkinsSJ/web-components

Convert UIWindowThemeDialog to components
Eric Dubé hai 1 ano
pai
achega
65b489883d

+ 2 - 8
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);

+ 2 - 8
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);

+ 2 - 8
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);

+ 2 - 8
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);

+ 2 - 8
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);

+ 2 - 8
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);

+ 2 - 2
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);
+defineComponent('c-recovery-code-entry', RecoveryCodeEntryView);

+ 2 - 8
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);

+ 108 - 0
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 <https://www.gnu.org/licenses/>.
+ */
+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*/`
+            <div class="slider">
+                <label class="slider-label">${html_encode(label)}</label>
+                <input class="slider-input" type="range" min="${min}" max="${max}" value="${value}" step="${step}">
+            </div>
+        `);
+    }
+
+    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);

+ 2 - 8
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);

+ 2 - 8
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);

+ 2 - 8
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);

+ 2 - 8
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);

+ 63 - 102
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 {};
 }

+ 0 - 31
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;

+ 8 - 0
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);
+    }
+};