/**
* SettingsPanel - Configurable settings modal for VTK viewers
*
* Generates and manages a settings panel with configurable fields.
* Different viewers can specify which settings to show.
*/
/**
* Available setting types
*/
export const SettingTypes = {
NUMBER: 'number',
COLOR: 'color',
SELECT: 'select',
CHECKBOX: 'checkbox'
};
/**
* Default settings configuration
*/
const DEFAULT_SETTINGS_CONFIG = [
{
id: 'pointSize',
label: 'Point Size',
type: SettingTypes.NUMBER,
min: 1,
max: 20,
step: 1,
default: 5
},
{
id: 'meshColor',
label: 'Mesh Color',
type: SettingTypes.COLOR,
default: '#ffffff'
},
{
id: 'edgeWidth',
label: 'Edge Width',
type: SettingTypes.NUMBER,
min: 1,
max: 10,
step: 1,
default: 1
},
{
id: 'representation',
label: 'Render Mode',
type: SettingTypes.SELECT,
options: [
{ value: '2', label: 'Surface' },
{ value: '1', label: 'Wireframe' },
{ value: '0', label: 'Points' }
],
default: '2'
},
{
id: 'parallelProjection',
label: 'Parallel Projection',
type: SettingTypes.CHECKBOX,
default: false
},
{
id: 'showAxisIndicator',
label: 'Show Axis Indicator',
type: SettingTypes.CHECKBOX,
default: false
}
];
/**
* Colormap range settings (optional, for field visualization)
*/
const COLORMAP_SETTINGS_CONFIG = [
{
id: 'autoColorRange',
label: 'Auto Range',
type: SettingTypes.CHECKBOX,
default: true,
section: 'Colormap Range'
},
{
id: 'colormapMin',
label: 'Min',
type: SettingTypes.NUMBER,
step: 'any',
placeholder: 'Auto',
default: '',
section: 'Colormap Range'
},
{
id: 'colormapMax',
label: 'Max',
type: SettingTypes.NUMBER,
step: 'any',
placeholder: 'Auto',
default: '',
section: 'Colormap Range'
}
];
export class SettingsPanel {
/**
* Create a SettingsPanel
* @param {Object} options - Configuration options
* @param {HTMLElement} options.container - Container element for the panel
* @param {Function} options.onApply - Callback when settings are applied
* @param {string[]} options.enabledSettings - Array of setting IDs to show (null = all)
* @param {boolean} options.includeColormapSettings - Whether to include colormap range settings
*/
constructor(options = {}) {
this.container = options.container || document.body;
this.onApply = options.onApply || (() => {});
this.enabledSettings = options.enabledSettings || null;
this.includeColormapSettings = options.includeColormapSettings || false;
this.panelElement = null;
this.overlayElement = null;
this.currentValues = {};
// Build settings config
this.settingsConfig = this._buildSettingsConfig();
// Initialize default values
this.settingsConfig.forEach(setting => {
this.currentValues[setting.id] = setting.default;
});
}
/**
* Build the settings configuration based on enabled settings
* @private
*/
_buildSettingsConfig() {
let config = [...DEFAULT_SETTINGS_CONFIG];
if (this.includeColormapSettings) {
config = config.concat(COLORMAP_SETTINGS_CONFIG);
}
if (this.enabledSettings) {
config = config.filter(s => this.enabledSettings.includes(s.id));
}
return config;
}
/**
* Generate the HTML for the settings panel
* @returns {string} HTML string
*/
generateHTML() {
let currentSection = null;
let html = `
Appearance Settings
`;
this.settingsConfig.forEach(setting => {
// Add section header if needed
if (setting.section && setting.section !== currentSection) {
currentSection = setting.section;
html += `
`;
}
html += this._generateSettingRow(setting);
});
html += `
`;
return html;
}
/**
* Generate HTML for a single setting row
* @private
*/
_generateSettingRow(setting) {
let inputHtml = '';
switch (setting.type) {
case SettingTypes.NUMBER:
inputHtml = `
`;
break;
case SettingTypes.COLOR:
inputHtml = ``;
break;
case SettingTypes.SELECT:
inputHtml = `';
break;
case SettingTypes.CHECKBOX:
const checked = setting.default ? 'checked' : '';
return `
`;
}
return `
${inputHtml}
`;
}
/**
* Render the panel into the container
*/
render() {
// Create wrapper if needed
const wrapper = document.createElement('div');
wrapper.innerHTML = this.generateHTML();
// Find or create panel elements in container
this.overlayElement = wrapper.querySelector('#settingsOverlay');
this.panelElement = wrapper.querySelector('#settingsPanel');
// Append to container
this.container.appendChild(this.overlayElement);
this.container.appendChild(this.panelElement);
// Setup event listeners
this._setupEventListeners();
}
/**
* Attach to existing panel elements in the DOM
* @param {HTMLElement} panel - The settings panel element
* @param {HTMLElement} overlay - The overlay element
*/
attachToExisting(panel, overlay) {
this.panelElement = panel;
this.overlayElement = overlay;
this._setupEventListeners();
}
/**
* Setup event listeners
* @private
*/
_setupEventListeners() {
// Apply button
const applyBtn = this.panelElement.querySelector('#applySettings');
if (applyBtn) {
applyBtn.addEventListener('click', () => this._handleApply());
}
// Cancel button
const cancelBtn = this.panelElement.querySelector('#cancelSettings');
if (cancelBtn) {
cancelBtn.addEventListener('click', () => this.close());
}
// Overlay click
if (this.overlayElement) {
this.overlayElement.addEventListener('click', () => this.close());
}
}
/**
* Handle apply button click
* @private
*/
_handleApply() {
const values = this.getValues();
this.currentValues = { ...values };
this.onApply(values);
this.close();
}
/**
* Open the settings panel
*/
open() {
// Update inputs with current values
this.setValues(this.currentValues);
if (this.panelElement) {
this.panelElement.classList.add('visible');
}
if (this.overlayElement) {
this.overlayElement.classList.add('visible');
}
}
/**
* Close the settings panel
*/
close() {
if (this.panelElement) {
this.panelElement.classList.remove('visible');
}
if (this.overlayElement) {
this.overlayElement.classList.remove('visible');
}
}
/**
* Get current values from all inputs
* @returns {Object} Object with setting id -> value pairs
*/
getValues() {
const values = {};
this.settingsConfig.forEach(setting => {
const element = document.getElementById(setting.id);
if (!element) return;
if (setting.type === SettingTypes.CHECKBOX) {
values[setting.id] = element.checked;
} else {
values[setting.id] = element.value;
}
});
return values;
}
/**
* Set values on all inputs
* @param {Object} values - Object with setting id -> value pairs
*/
setValues(values) {
this.settingsConfig.forEach(setting => {
const element = document.getElementById(setting.id);
if (!element || values[setting.id] === undefined) return;
if (setting.type === SettingTypes.CHECKBOX) {
element.checked = values[setting.id];
} else {
element.value = values[setting.id];
}
});
}
/**
* Update stored current values
* @param {Object} values - Partial values to update
*/
updateCurrentValues(values) {
this.currentValues = { ...this.currentValues, ...values };
}
/**
* Get current stored values
* @returns {Object} Current values
*/
getCurrentValues() {
return { ...this.currentValues };
}
}
export default SettingsPanel;