From 9267b506662df5c932ca02812290b8d544487b4a Mon Sep 17 00:00:00 2001 From: Sam Atkins Date: Wed, 8 May 2024 13:06:08 +0100 Subject: [PATCH] Convert UIWindowThemeDialog to components This also fixes the "Reset Colors" button not adjusting the sliders. --- src/UI/Components/Slider.js | 114 +++++++++++++++++++++++ src/UI/UIWindowThemeDialog.js | 165 +++++++++++++--------------------- src/css/style.css | 31 ------- 3 files changed, 177 insertions(+), 133 deletions(-) create mode 100644 src/UI/Components/Slider.js diff --git a/src/UI/Components/Slider.js b/src/UI/Components/Slider.js new file mode 100644 index 00000000..11d2516a --- /dev/null +++ b/src/UI/Components/Slider.js @@ -0,0 +1,114 @@ +/** + * 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 } 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; + }); + } +} + +// 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_slider ) { + window.__component_slider = true; + + customElements.define('c-slider', Slider); +} \ No newline at end of file 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;