consolidate modal handling into a new core file

This commit is contained in:
Stewart Allen 2025-12-24 18:01:58 -05:00
commit 79f7a33823
3 changed files with 127 additions and 64 deletions

View file

@ -6,6 +6,7 @@ import { base } from '../../geo/base.js';
import { local as sdb } from '../../data/local.js';
import { slider } from './slider.js';
import { keyboard } from './keyboard.js';
import { modal } from './modal.js';
import { space } from '../../moto/space.js';
import { LOCAL, SETUP } from './main.js';
import { VIEWS, MODES, SEED } from './consts.js';
@ -848,6 +849,23 @@ function init_one() {
settingsSave(undefined, ui.settingsName.value);
};
// initialize modal controller
modal.init({
ui: {
modal: ui.modal,
modals: ui.modals
},
onShow: (which) => {
api.event.emit('modal.show', which);
},
onHide: () => {
api.event.emit('modal.hide');
}
});
// expose modal to API
api.modal = modal;
// slider setup
slider.init({
ui: {

View file

@ -4,6 +4,7 @@ import './frame.js';
import '../../ext/base64.js';
import STACKS from './stacks.js';
import { modal } from './modal.js';
import { $ } from '../../moto/webui.js';
import { api } from './api.js';
@ -49,8 +50,8 @@ stats.set('kiri', version);
self.kiri_catalog = FILES;
export const dialog = {
show: showModal,
hide: hideModal,
show: (which) => modal.show(which),
hide: () => modal.hide(),
update_process_list: updateProcessList
};
@ -84,11 +85,8 @@ export const image = {
convert: loadImageConvert
};
export const modal = {
show: showModal,
hide: hideModal,
visible: modalShowing
};
// Re-export modal singleton methods
export { modal };
export const mode = {
get_id() { return MODE },
@ -172,7 +170,7 @@ export const view = {
};
// add show() to catalog for API
FILES.show = showCatalog;
FILES.show = () => modal.show('files');
// patch broker for api backward compatibility
EVENT.on = (topic, listener) => {
@ -604,59 +602,6 @@ function clearWorkspace() {
platform.delete(selection.meshes());
}
function modalShowing() {
return api.ui.modal.style.display === 'flex';
}
function showModal(which) {
let mod = api.ui.modal,
style = mod.style,
visible = modalShowing(),
info = { pct: 0 };
// hide all modals befroe showing another
Object.keys(api.ui.modals).forEach(name => {
api.ui.modals[name].style.display = name === which ? 'flex' : '';
});
function ondone() {
api.event.emit('modal.show', which);
}
if (visible) {
return ondone();
}
style.height = '0';
style.display = 'flex';
new TWEEN.Tween(info).
easing(TWEEN.Easing.Quadratic.InOut).
to({ pct: 100 }, 100).
onUpdate(() => { style.height = `${info.pct}%` }).
onComplete(ondone).
start();
}
function hideModal() {
if (!modalShowing()) {
return;
}
let mod = api.ui.modal, style = mod.style, info={pct:100};
new TWEEN.Tween(info).
easing(TWEEN.Easing.Quadratic.InOut).
to({pct:0}, 100).
onUpdate(() => { style.height = `${info.pct}%` }).
onComplete(() => {
style.display = '';
api.event.emit('modal.hide');
}).
start();
}
function showCatalog() {
showModal("files");
}
function editSettings(e) {
let current = settings.get(),
@ -729,7 +674,7 @@ function updateProcessList() {
load.onclick = (ev) => {
api.conf.load(undefined, sk);
updateProcessList();
hideModal();
modal.hide();
}
load.appendChild(DOC.createTextNode(sk));
if (sk == settings.proc().processName) {
@ -772,7 +717,7 @@ function showHelpFile(local,then) {
}
const LANG = api.language.current;
$('kiri-version').innerHTML = `${LANG.version} ${version}`;
showModal('help');
modal.show('help');
api.event.emit('help.show', local);
}
@ -881,7 +826,7 @@ function setMode(mode, lock, then) {
selection.update_bounds(api.widgets.all());
api.conf.update_fields();
// because device dialog, if showing, needs to be updated
if (modalShowing()) {
if (modal.visible()) {
api.show.devices();
}
api.space.restore(null, true);

100
src/kiri/core/modal.js Normal file
View file

@ -0,0 +1,100 @@
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
/**
* Modal Manager (Singleton)
* Manages modal dialog display and animations
*/
class ModalControl {
#ui;
#onShow;
#onHide;
#initialized = false;
/**
* Initialize the modal controller with dependencies
* @param {Object} config
* @param {Object} config.ui - UI element references
* @param {HTMLElement} config.ui.modal - Modal container element
* @param {Object} config.ui.modals - Object containing all modal elements by name
* @param {Function} config.onShow - Optional callback when modal is shown
* @param {Function} config.onHide - Optional callback when modal is hidden
*/
init({ ui, onShow, onHide }) {
if (this.#initialized) {
throw new Error('Modal already initialized');
}
this.#ui = ui;
this.#onShow = onShow;
this.#onHide = onHide;
this.#initialized = true;
}
/**
* Check if modal is currently visible
* @returns {boolean}
*/
visible() {
return this.#ui.modal.style.display === 'flex';
}
/**
* Show a specific modal with animation
* @param {string} which - Name of the modal to show
*/
show(which) {
let mod = this.#ui.modal,
style = mod.style,
visible = this.visible(),
info = { pct: 0 };
// hide all modals before showing another
Object.keys(this.#ui.modals).forEach(name => {
this.#ui.modals[name].style.display = name === which ? 'flex' : '';
});
const ondone = () => {
this.#onShow?.(which);
};
if (visible) {
return ondone();
}
style.height = '0';
style.display = 'flex';
new TWEEN.Tween(info).
easing(TWEEN.Easing.Quadratic.InOut).
to({ pct: 100 }, 100).
onUpdate(() => { style.height = `${info.pct}%` }).
onComplete(ondone).
start();
}
/**
* Hide the modal with animation
*/
hide() {
if (!this.visible()) {
return;
}
let mod = this.#ui.modal,
style = mod.style,
info = { pct: 100 };
new TWEEN.Tween(info).
easing(TWEEN.Easing.Quadratic.InOut).
to({ pct: 0 }, 100).
onUpdate(() => { style.height = `${info.pct}%` }).
onComplete(() => {
style.display = '';
this.#onHide?.();
}).
start();
}
}
// Export singleton instance
export const modal = new ModalControl();