// Options picker import { OPTION_GROUP_CLASSNAME, TALL_CLASSNAME, TOOL_INFO_CLASSNAME, WIDE_CLASSNAME, } from '../config.js'; import {Tool} from '../tool.js'; import {show, hide} from '../helper.js'; export class OptionsTool extends Tool { sectionNames = undefined; groups = {}; constructor(sectionNames) { super(); this.sectionNames = sectionNames; } setContainer(container) { super.setContainer(container); // Initialize for (const sectionName of this.sectionNames) { const group = this.sim.options.getSection(sectionName); const item = this.visitItem(group); this.div.appendChild(item); } } // For now, `showIf` must be the name of a boolean property, with optional negation shouldShow(option) { if (option.showIf === undefined) return true; const {name, value} = this.deconstructOption(option.showIf); return this.sim.getOption(name) === value; } deconstructOption(showIf) { let name = showIf; let value = true; if (name.startsWith('!')) { value = false; name = name.slice(1); } return {name, value}; } visitItem(item, path) { path = [path, item.name].filter(x => !!x).join('.'); switch (item.type) { case 'group': { const groupEl = document.createElement('div'); groupEl.classList.add(OPTION_GROUP_CLASSNAME); const group = {groupEl, items: []}; this.groups[path] = group; if (item.title) { const heading = document.createElement('h3'); heading.innerHTML = item.title; groupEl.appendChild(heading); groupEl.items.push({itemEl: heading}); } for (const next of item.items) { const optionEl = this.visitItem(next, path); group.items.push(next); if (this.shouldShow(next)) { groupEl.appendChild(optionEl); } if (next.showIf) { const {name} = this.deconstructOption(next.showIf); this.sim.onOptionSet(name, () => { if (this.shouldShow(next)) { show({ items: group.items, item: next, parentEl: groupEl, itemEl: optionEl, }); } else { hide({ items: group.items, item: next, parentEl: groupEl, itemEl: optionEl, }); } }); } } return groupEl; } case 'boolean': { const button = document.createElement('button'); button.innerHTML = item.title; if (item.wide === true) button.classList.add(WIDE_CLASSNAME); if (item.tall === true) button.classList.add(TALL_CLASSNAME); const value = this.sim.getOption(path); button.style.opacity = value ? '100%' : '50%'; this.sim.onOptionSet(path, ({value}) => { button.style.opacity = value ? '100%' : '50%'; }); button.addEventListener('click', () => { const value = this.sim.getOption(path); this.sim.setOption(path, !value); }); return button; } case 'number': { const div = document.createElement('div'); const title = document.createElement('button'); const input = document.createElement('input'); const maxLength = item.maxLength || 8; div.appendChild(title); div.appendChild(input); div.classList.add(WIDE_CLASSNAME); title.classList.add(TOOL_INFO_CLASSNAME); if (item.wide) { title.classList.add(WIDE_CLASSNAME); input.classList.add(WIDE_CLASSNAME); } title.innerHTML = item.title; input.value = this.sim.getOption(path); input.addEventListener('input', () => { input.value = input.value.slice(0, maxLength); }); input.addEventListener('change', () => { this.sim.setOption(path, input.value); }); this.sim.onOptionSet(path, ({value}) => { input.value = value; }); return div; } default: throw new Error('Unknown option type'); } } }