From 26bea5daae28e837575195aa8ea4723fd46dee71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20P=2E?= Date: Tue, 21 Apr 2026 15:30:35 +0200 Subject: [PATCH] Move UI logic to Stimulus: toolbar only --- js/controllers/clipper_controller.js | 74 ++++++++++++ js/controllers/settings_controller.js | 28 +++++ js/controllers/tabs_controller.js | 42 +++++++ js/controllers/toolbar_controller.js | 50 ++++++++ js/ui.js | 157 -------------------------- js/utils/environment.js | 30 ++++- js/utils/stimulus.js | 14 +++ scenes/ssgp/index.html | 56 +++++++-- scenes/ssgp/index.js | 3 + 9 files changed, 288 insertions(+), 166 deletions(-) create mode 100644 js/controllers/clipper_controller.js create mode 100644 js/controllers/settings_controller.js create mode 100644 js/controllers/tabs_controller.js create mode 100644 js/controllers/toolbar_controller.js create mode 100644 js/utils/stimulus.js diff --git a/js/controllers/clipper_controller.js b/js/controllers/clipper_controller.js new file mode 100644 index 0000000..07551a2 --- /dev/null +++ b/js/controllers/clipper_controller.js @@ -0,0 +1,74 @@ +// Global ATON +import { Controller } from "@hotwired/stimulus" +import AppState from "../state.js"; +import { addClippingPlane, resetClipping } from "../utils/clipping.js"; +import { toggleAmbientOcclusion } from "../utils/environment.js"; + +/** + * Handle events for the clipper toolbar, + * related to the clipping module + */ +export default class extends Controller { + static targets = ['trigger', 'clipper', 'axis']; + static values = { enabled: Boolean }; + + connect() { + console.log('#clipper controller connected'); + } + + clip(event) { + /** + * @type {string} + */ + const label = event.params.axis; + /** + * @type {HTMLButtonElement} + */ + const target = event.target; + /** + * @type {NodeListOf} + */ + const axes = this.axisTargets; + const classes = ['border', 'border-2', 'border-warning']; + + addClippingPlane(label, -1); + target.classList.add(...classes); + + for (const btn of axes) { + if (btn.id !== target.id) { + btn.classList.remove(...classes); + } + } + } + /** + * Toggle clipper toolbar + */ + toggleClipper() { + /** + * @type {HTMLElement} + */ + const trigger = this.triggerTarget; + this.clipperTarget.classList.toggle('d-none'); + // If the toolbar is shown, clipping is enabled and vice versa + this.enabledValue = !this.clipperTarget.classList.contains('d-none'); + + this.axisTargets.forEach(btn => { + btn.classList.remove('border', 'border-2', 'border-warning'); + }); + + // AO should be turned off if clipping is enabled + toggleAmbientOcclusion(!this.enabledValue); + + if (this.enabledValue) { + trigger.className += ' border border-2 border-white'; + } + + if (!this.enabledValue) { + resetClipping(); + trigger.className = trigger.className.replace(/ border.*$/g, ''); + } + + AppState.clipping.enabled = this.enabledValue; + } +} + diff --git a/js/controllers/settings_controller.js b/js/controllers/settings_controller.js new file mode 100644 index 0000000..17491c8 --- /dev/null +++ b/js/controllers/settings_controller.js @@ -0,0 +1,28 @@ +import { Controller } from "@hotwired/stimulus" +import AppState from "../state.js"; + +export default class extends Controller { + static targets = ['ao', 'shadows']; + + connect() { + console.log('#settings controller connected'); + this.aoTarget.checked = AppState.ambientOcclusion; + this.shadowsTarget.checked = AppState.shadows; + } + /** + * Toggle Ambient Occlusion + * @param {Event} event + */ + toggleAO(event) { + ATON.FX.togglePass(ATON.FX.PASS_AO, event.target.checked); + AppState.ambientOcclusion = event.target.checked; + } + /** + * Toggle shadows + * @param {Event} event + */ + toggleShadows(event) { + ATON.toggleShadows(event.target.checked); + AppState.shadows = event.target.checked; + } +} diff --git a/js/controllers/tabs_controller.js b/js/controllers/tabs_controller.js new file mode 100644 index 0000000..df79146 --- /dev/null +++ b/js/controllers/tabs_controller.js @@ -0,0 +1,42 @@ +import { Controller } from "@hotwired/stimulus" + +export default class extends Controller { + static targets = ['active', 'tab', 'content']; + + connect() { + console.log('#tabs controller connected'); + } + /** + * + * @param {Event} event + */ + activate(event) { + event.preventDefault(); + this.deactivate(); + const activeId = event.currentTarget.dataset.id; + + event.currentTarget.parentElement.classList.add('active'); + this.contentTargets.find(c => c.dataset.id === activeId) + .classList.remove('d-hide'); + } + + reset() { + this.deactivate(); + const activeId = this.activeTarget.dataset.id; + + this.activeTarget.classList.add('active'); + this.contentTargets.find(c => c.dataset.id === activeId) + .classList.remove('d-hide'); + } + + deactivate() { + this.tabTargets.forEach(tab => { + tab.classList.remove('active'); + }); + + this.contentTargets.forEach(content => { + content.classList.add('d-hide'); + }); + } +} + diff --git a/js/controllers/toolbar_controller.js b/js/controllers/toolbar_controller.js new file mode 100644 index 0000000..b181e58 --- /dev/null +++ b/js/controllers/toolbar_controller.js @@ -0,0 +1,50 @@ +// Global ATON +import { Controller } from "@hotwired/stimulus" +import AppState from "../state.js"; +import { createLightSlider } from "../utils/environment.js"; + +const html = String.raw; +const panelHeader = html` + Impostazioni +`; + +export default class extends Controller { + static targets = ['settings']; + + connect() { + console.log('#toolbar controller connected'); + } + /** + * Open settings panel + * @param {Event} event + */ + toggleSettings(event) { + ATON.UI.setSidePanelLeft(); + ATON.UI.showSidePanel({header: panelHeader}); + this.#buildSettingsPanel(ATON.UI.elSidePanel); + } + /** + * Clone a