/*jshint esversion: 11 */
// @ts-check
/**
* CS559 3D World Framework Code
*
* Simple, automatic UI from an object with properly declared parameters
*
* @module AutoUI
* */
// we need to have the BaseClass definition
import { GrObject } from "./GrObject.js";
// we need to import the module to get its typedefs for the type checker
import * as InputHelpers from "../CS559/inputHelpers.js";
/**
* This is a "global" variable - if panels are placed without a where,
* we make a DIV (the "panel panel") and put them in there - this way
* we can get floating
*
* @type{HTMLElement}
*/
let panelPanel;
// since exports are read only, always access it by a function that will make it
export function panel() {
if (!panelPanel) {
panelPanel = InputHelpers.makeFlexDiv();
panelPanel.id = "panel-panel";
}
return panelPanel;
}
export class AutoUI {
/**
* Create a UI panel for a GrObject
* goes through the parameters and makes a slider for each
* also defines a callback for those sliders that calls the
* object's update function.
*
* This does place the panel into the DOM (onto the web page)
* using `insertElement` in the CS559 helper library. The place
* it is placed is controlled the `where` parameter. By default,
* it goes at the end of the DOM. However, you can pass it a DOM
* element to be placed inside (or some other choices as well).
*
* @param {GrObject} object
* @param {number} [width=300]
* @param {InputHelpers.WhereSpec} [where] - where to place the panel in the DOM (at the end of the page by default)
* @param {boolean} adjusted - whether adjust the slider length according to the label length
* @param {string} display - align type of the label and slider
*/
constructor(object, width = 300, where = undefined, widthdiv = 1, adjusted = false, display = "inline-block") {
const self = this;
this.object = object;
/* if no where is provided, put it at the end of the panel panel - assuming there is one */
if (!where) {
where = panel();
}
this.div = InputHelpers.makeBoxDiv({ width: width, flex: widthdiv>1 }, where);
InputHelpers.makeHead(object.name, this.div, { tight: true });
if (widthdiv > 1) InputHelpers.makeFlexBreak(this.div);
this.sliders = object.params.map(function(param) {
const slider = new InputHelpers.LabelSlider(param.name, {
where: self.div,
width: (width / widthdiv) - 20,
min: param.min,
max: param.max,
step: param.step ?? ((param.max - param.min) / 30),
initial: param.initial,
id: object.name + "-" + param.name,
adjusted: adjusted,
display: display,
});
return slider;
});
this.sliders.forEach(function(sl) {
sl.oninput = function() {
self.update();
};
});
this.update();
}
update() {
const vals = this.sliders.map(sl => Number(sl.value()));
this.object.update(vals);
}
/**
*
* @param {number | string} param
* @param {number} value
*/
set(param, value) {
if (typeof param === "string") {
for (let i = 0; i < this.object.params.length; i++) {
if (param == this.object.params[i].name) {
this.sliders[i].set(Number(value));
return;
}
}
throw `Bad parameter ${param} to set`;
} else {
this.sliders[param].set(Number(value));
}
}
}