From 8e083d20d2dee4e3050b26cc285a6a14efa16c3d Mon Sep 17 00:00:00 2001 From: KernelDeimos Date: Thu, 30 May 2024 15:59:05 -0400 Subject: [PATCH 1/6] tweak(ui): allow size changing on spinner --- src/UI/Components/Spinner.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/UI/Components/Spinner.js b/src/UI/Components/Spinner.js index 05d1e20d9..ffae866ab 100644 --- a/src/UI/Components/Spinner.js +++ b/src/UI/Components/Spinner.js @@ -3,15 +3,19 @@ const Component = use('util.Component'); export default def(class Spinner extends Component { static ID = 'ui.component.Spinner'; - static PROPERTIES = {} + static PROPERTIES = { + size: { + value: 24, + }, + } // static RENDER_MODE = Component.NO_SHADOW; create_template ({ template }) { - console.log('template?', template); + const size = '' + Number(this.get('size')); template.innerHTML = /*html*/`
- + circle anim From 38ba42575ce9f3506f8ce219b9580202b3ed9993 Mon Sep 17 00:00:00 2001 From: KernelDeimos Date: Thu, 30 May 2024 16:03:03 -0400 Subject: [PATCH 2/6] feat(ui): add new components This commit adds the following components: - Glyph --- src/UI/Components/Glyph.js | 28 ++++++++++++++++++++++++++++ src/init_async.js | 1 + 2 files changed, 29 insertions(+) create mode 100644 src/UI/Components/Glyph.js diff --git a/src/UI/Components/Glyph.js b/src/UI/Components/Glyph.js new file mode 100644 index 000000000..2d58dadfc --- /dev/null +++ b/src/UI/Components/Glyph.js @@ -0,0 +1,28 @@ +import { Component } from "../../util/Component.js"; + +export default def(class Glyph extends Component { + static ID = 'ui.component.Glyph'; + + static PROPERTIES = { + size: { + value: 24, + }, + codepoint: { + value: '✅', + }, + } + + static CSS = ` + div { + text-align: center; + } + `; + + create_template ({ template }) { + template.innerHTML = /*html*/` +
+ ${this.get('codepoint')} +
+ `; + } +}); diff --git a/src/init_async.js b/src/init_async.js index 212310431..b738b9aa9 100644 --- a/src/init_async.js +++ b/src/init_async.js @@ -3,6 +3,7 @@ logger.info('start -> async initialization'); import './util/TeePromise.js'; import './util/Component.js'; +import './UI/Components/Glyph.js'; logger.info('end -> async initialization'); globalThis.init_promise.resolve(); From f8780d032b10138851c22af53b8610c578139acc Mon Sep 17 00:00:00 2001 From: KernelDeimos Date: Thu, 30 May 2024 16:07:16 -0400 Subject: [PATCH 3/6] fix(ui): improve Component base class The following improvements were made: - do not require empty object in constructor - allow components to override render mode based on constructor values - improve error handling in get() - allow array of property names in on_ready()->listen() - allow components to be attached to shadow roots --- src/util/Component.js | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/src/util/Component.js b/src/util/Component.js index 9b08ba12e..813ce00b7 100644 --- a/src/util/Component.js +++ b/src/util/Component.js @@ -34,14 +34,23 @@ export const Component = def(class Component extends HTMLElement { }); } - constructor (property_values) { - super(); - + _set_dom_based_on_render_mode () { if ( this.constructor.RENDER_MODE === Component.NO_SHADOW ) { this.dom_ = this; } else { this.dom_ = this.attachShadow({ mode: 'open' }); } + } + + constructor (property_values) { + super(); + + property_values = property_values || {}; + + // We allow a subclass of component to define custom behavior + // for the `RENDER_MODE` static property. This is so JustHTML + // can have ths `no_shadow: true` option. + this._set_dom_based_on_render_mode({ property_values }); this.values_ = {}; @@ -104,6 +113,10 @@ export const Component = def(class Component extends HTMLElement { } get (key) { + if ( ! this.values_.hasOwnProperty(key) ) { + throw new Error(`Unknown property \`${key}\` in ${ + this.constructor.ID || this.constructor.name}`); + } return this.values_[key].get(); } @@ -130,6 +143,11 @@ export const Component = def(class Component extends HTMLElement { return; } + if ( destination instanceof ShadowRoot ) { + destination.appendChild(this); + return; + } + if ( destination.$ === 'placeholder' ) { destination.replaceWith(this); return; @@ -162,6 +180,14 @@ export const Component = def(class Component extends HTMLElement { get_api_ () { return { listen: (name, callback) => { + if ( Array.isArray(name) ) { + const names = name; + for ( const name of names ) { + this.values_[name].sub((_, more) => { + callback(this, { ...more, name }); + }); + } + } this.values_[name].sub(callback); callback(this.values_[name].get(), {}); } From 577bd59b6cc94810e851ad544f8234e25a4e6e27 Mon Sep 17 00:00:00 2001 From: KernelDeimos Date: Thu, 30 May 2024 16:11:13 -0400 Subject: [PATCH 4/6] feat(ui): add new components This commit adds the following components: - ActionCard - Frame - NotifCard --- src/UI/Components/ActionCard.js | 39 +++++++++++++++++++++++++++++++++ src/UI/Components/Frame.js | 20 +++++++++++++++++ src/UI/Components/NotifCard.js | 25 +++++++++++++++++++++ src/css/style.css | 4 ++++ src/init_async.js | 3 +++ 5 files changed, 91 insertions(+) create mode 100644 src/UI/Components/ActionCard.js create mode 100644 src/UI/Components/Frame.js create mode 100644 src/UI/Components/NotifCard.js diff --git a/src/UI/Components/ActionCard.js b/src/UI/Components/ActionCard.js new file mode 100644 index 000000000..8c65b9118 --- /dev/null +++ b/src/UI/Components/ActionCard.js @@ -0,0 +1,39 @@ +const Component = use('util.Component'); + +export default def(class ActionCard extends Component { + static ID = 'ui.component.ActionCard'; + static RENDER_MODE = Component.NO_SHADOW; + + static PROPERTIES = { + title: { + value: 'Title' + }, + info: {}, + button_text: {}, + button_style: {}, + on_click: {}, + style: {}, + } + + create_template ({ template }) { + $(template).html(/*html*/` +
+
+ ${ this.get('title') } + ${ + this.get('info') + } +
+
+ +
+
+ `); + } + + on_ready ({ listen }) { + $(this.dom_).find('button').on('click', this.get('on_click') || (() => {})); + } +}); diff --git a/src/UI/Components/Frame.js b/src/UI/Components/Frame.js new file mode 100644 index 000000000..446f4f166 --- /dev/null +++ b/src/UI/Components/Frame.js @@ -0,0 +1,20 @@ +const Component = use('util.Component'); + +export default def(class Frame extends Component { + static ID = 'ui.component.Frame'; + static RENDER_MODE = Component.NO_SHADOW; + + static PROPERTIES = { + component: {}, + } + + on_ready ({ listen }) { + listen('component', component => { + this.dom_.innerHTML = ''; + if ( ! component ) { + return; + } + component.attach(this.dom_); + }); + } +}); diff --git a/src/UI/Components/NotifCard.js b/src/UI/Components/NotifCard.js new file mode 100644 index 000000000..737904e21 --- /dev/null +++ b/src/UI/Components/NotifCard.js @@ -0,0 +1,25 @@ +const Component = use('util.Component'); + +export default def(class NotifCard extends Component { + static ID = 'ui.component.NotifCard'; + static RENDER_MODE = Component.NO_SHADOW; + + static PROPERTIES = { + text: { value: 'no text' }, + style: {}, + } + + create_template ({ template }) { + $(template).html(/*html*/` +
+
+ ${ this.get('text') } +
+
+ `); + } + + on_ready ({ listen }) { + $(this.dom_).find('button').on('click', this.get('on_click') || (() => {})); + } +}); diff --git a/src/css/style.css b/src/css/style.css index 2c2726635..7306345bb 100644 --- a/src/css/style.css +++ b/src/css/style.css @@ -3708,6 +3708,10 @@ fieldset[name=number-code] { height: 45px; } +.thin-card { + padding: 0 15px; +} + .settings-card strong { font-weight: 500; } diff --git a/src/init_async.js b/src/init_async.js index b738b9aa9..40aca629c 100644 --- a/src/init_async.js +++ b/src/init_async.js @@ -3,7 +3,10 @@ logger.info('start -> async initialization'); import './util/TeePromise.js'; import './util/Component.js'; +import './UI/Components/Frame.js'; import './UI/Components/Glyph.js'; +import './UI/Components/ActionCard.js'; +import './UI/Components/NotifCard.js'; logger.info('end -> async initialization'); globalThis.init_promise.resolve(); From be38df32ec8aab1047a4da0107050f948742d091 Mon Sep 17 00:00:00 2001 From: KernelDeimos Date: Thu, 30 May 2024 16:12:01 -0400 Subject: [PATCH 5/6] tweak(ui): allow setting render more for JustHTML --- src/UI/Components/JustHTML.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/UI/Components/JustHTML.js b/src/UI/Components/JustHTML.js index b1233d695..ace8548f9 100644 --- a/src/UI/Components/JustHTML.js +++ b/src/UI/Components/JustHTML.js @@ -15,4 +15,13 @@ export default def(class JustHTML extends Component { $(this.dom_).find('span').html(html); }); } + + _set_dom_based_on_render_mode({ property_values }) { + if ( property_values.no_shadow ) { + this.dom_ = this; + return; + } + + return super._set_dom_based_on_render_mode(); + } }); From 124596058a286241b51dd87ce2fc1a68478cb5b8 Mon Sep 17 00:00:00 2001 From: KernelDeimos Date: Thu, 30 May 2024 16:13:13 -0400 Subject: [PATCH 6/6] feat(ui): allow component-based settings tabs --- src/UI/Settings/UIWindowSettings.js | 21 +++++++++++++++++---- src/init_sync.js | 3 ++- src/util/Placeholder.js | 4 ++-- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/UI/Settings/UIWindowSettings.js b/src/UI/Settings/UIWindowSettings.js index 832ea8f6a..e50999511 100644 --- a/src/UI/Settings/UIWindowSettings.js +++ b/src/UI/Settings/UIWindowSettings.js @@ -17,6 +17,7 @@ * along with this program. If not, see . */ +import Placeholder from '../../util/Placeholder.js'; import UIWindow from '../UIWindow.js' async function UIWindowSettings(options){ @@ -26,6 +27,7 @@ async function UIWindowSettings(options){ const svc_settings = globalThis.services.get('settings'); const tabs = svc_settings.get_tabs(); + const tab_placeholders = []; let h = ''; @@ -42,9 +44,14 @@ async function UIWindowSettings(options){ h += `
`; tabs.forEach((tab, i) => { - h += `
- ${tab.html()} -
`; + h += `
`; + if ( tab.factory ) { + tab_placeholders[i] = Placeholder(); + h += tab_placeholders[i].html; + } else { + h += tab.html(); + } + h += `
`; }); h += `
`; @@ -85,7 +92,13 @@ async function UIWindowSettings(options){ } }); const $el_window = $(el_window); - tabs.forEach(tab => tab.init($el_window)); + tabs.forEach((tab, i) => { + tab.init && tab.init($el_window); + if ( tab.factory ) { + const component = tab.factory(); + component.attach(tab_placeholders[i]); + } + }); $(el_window).on('click', '.settings-sidebar-item', function(){ const $this = $(this); diff --git a/src/init_sync.js b/src/init_sync.js index 44277a2f6..cea2e5347 100644 --- a/src/init_sync.js +++ b/src/init_sync.js @@ -84,7 +84,8 @@ logger.info('start -> blocking initialization'); } if ( registry_.classes_m[id] ) { - throw new Error(`Class with ID ${id} already registered`); + // throw new Error(`Class with ID ${id} already registered`); + return; } registry_.classes_m[id] = cls; diff --git a/src/util/Placeholder.js b/src/util/Placeholder.js index 48bf60197..5515a9f7b 100644 --- a/src/util/Placeholder.js +++ b/src/util/Placeholder.js @@ -18,7 +18,7 @@ * * @returns {PlaceholderReturn} */ -const Placeholder = () => { +const Placeholder = def(() => { const id = Placeholder.get_next_id_(); return { $: 'placeholder', @@ -29,7 +29,7 @@ const Placeholder = () => { place.replaceWith(el); } }; -}; +}, 'util.Placeholder'); const anti_collision = `94d2cb6b85a1`; // Arbitrary random string Placeholder.next_id_ = 0;