diff --git a/helper.js b/helper.js
index 33d0443..3517273 100644
--- a/helper.js
+++ b/helper.js
@@ -1,2 +1,38 @@
-export function makeUtilityButton() {
+// `items` is an array of which `item` is a member
+// `item` must let us read/write property `hidden`
+// `parentEl` is the containing element for `itemEl`
+// `itemEl` is the
+export function show({items, item, parentEl, itemEl}) {
+ if (items.length < 2) {
+ parentEl.appendChild(itemEl);
+ return;
+ }
+ // To determine placement,
+ // Start with our index in the toolbar tools;
+ // iterate through toolbar tools before this one,
+ // and subtract hidden ones from the index.
+
+ let countHidden = 0;
+ let index = items.indexOf(item);
+ for (let i = 0; i < index; i++) {
+ const sibling = items[i];
+ if (sibling.hidden) countHidden += 1;
+ }
+ index -= countHidden;
+
+ // Now we need to find our place.
+ // Add to parent using insertBefore.
+ let idx = 0;
+ let nextEl = parentEl.firstChild;
+ while (idx < index) {
+ nextEl = nextEl.nextSibling;
+ idx += 1;
+ }
+ parentEl.insertBefore(itemEl, nextEl);
+ item.hidden = false;
+}
+
+export function hide({item, parentEl, itemEl}) {
+ parentEl.removeChild(itemEl);
+ item.hidden = true;
}
diff --git a/sim-options.js b/sim-options.js
index 598f2be..f4a6cd5 100644
--- a/sim-options.js
+++ b/sim-options.js
@@ -8,9 +8,9 @@ export const simOptions = {
velocity: ['Velocity Vectors', 'boolean', true],
acceleration: ['Accel Vectors', 'boolean', true],
traces: ['Path Traces', 'boolean', true],
- dashedTraces: ['Dashed', 'boolean', false, {tall: true, hideUnless: 'display.traces'}],
- velocityScale: ['Velocity
Vec Scale', 'number', 80, {hideUnless: 'display.velocity'}],
- accelerationScale: ['Accel
Vec Scale', 'number', 800, {hideUnless: 'display.acceleration'}],
+ dashedTraces: ['Dashed', 'boolean', false, {tall: true, showIf: 'display.traces'}],
+ velocityScale: ['Velocity
Vec Scale', 'number', 80, {showIf: 'display.velocity'}],
+ accelerationScale: ['Accel
Vec Scale', 'number', 800, {showIf: 'display.acceleration'}],
zoomVectors: ['Zoom Vectors', 'boolean', true]
},
compensate: {
diff --git a/tool.js b/tool.js
index 0513e7b..91a7b2d 100644
--- a/tool.js
+++ b/tool.js
@@ -9,6 +9,7 @@ import {
export class Tool {
container = undefined;
sim = undefined;
+ hidden = false;
constructor() {
const div = document.createElement('div');
diff --git a/tool/options.js b/tool/options.js
index fe04a1e..fa81043 100644
--- a/tool/options.js
+++ b/tool/options.js
@@ -1,45 +1,92 @@
// Options picker
import {
- TOOL_INFO_CLASSNAME,
OPTION_GROUP_CLASSNAME,
- WIDE_CLASSNAME,
TALL_CLASSNAME,
+ TOOL_INFO_CLASSNAME,
+ WIDE_CLASSNAME,
} from '../config.js';
-import { Tool } from '../tool.js';
+import {Tool} from '../tool.js';
+import {show, hide} from '../helper.js';
export class OptionsTool extends Tool {
- sections = undefined;
+ sectionNames = undefined;
+ groups = {};
- constructor(sections) {
+ constructor(sectionNames) {
super();
- this.sections = sections;
+ this.sectionNames = sectionNames;
}
setContainer(container) {
super.setContainer(container);
- for (const sectionName of this.sections) {
- const option = this.sim.options.getSection(sectionName);
- const item = this.visitItem(option);
+ // 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 group = document.createElement('div');
- group.classList.add(OPTION_GROUP_CLASSNAME);
+ 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;
- group.appendChild(heading);
+ groupEl.appendChild(heading);
+ groupEl.items.push({itemEl: heading});
}
for (const next of item.items) {
- const child = this.visitItem(next, path);
- group.appendChild(child);
+ const optionEl = this.visitItem(next, path);
+ // const option = {itemEl: optionEl};
+ 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({
+ item: next,
+ parentEl: groupEl,
+ itemEl: optionEl,
+ });
+ }
+ });
+ }
+
}
- return group;
+ return groupEl;
}
case 'boolean': {
const button = document.createElement('button');