src/view/controls.js
- export const CONTROLS = {
- stepBy,
- isDuplicatePart,
- duplicateSourceIndex,
- clampVoicePitchRange,
- setupChordTonesTristate,
- setupFeedbackTristates,
- };
- function stepBy(params, descriptor, delta) {
- const { name, min, max } = descriptor;
- const v = params[name] + delta;
- params[name] = Math.max(min, Math.min(max, v));
- }
- function isDuplicatePart(parts, index, counter) {
- const inSection = counter === "parts" || counter === "step1.parts";
- return inSection && duplicateSourceIndex(parts, index) > 0;
- }
- function duplicateSourceIndex(parts, index) {
- const list = parts ?? [];
- const label = list[index]?.label;
- return list.slice(0, index).findIndex((p) => label && p?.label === label) + 1;
- }
- function clampVoicePitchRange(voice, fieldName) {
- if (voice.max_pitch < voice.min_pitch) {
- const other = fieldName === "min_pitch" ? "max_pitch" : "min_pitch";
- voice[fieldName] = voice[other];
- }
- }
- function setupChordTonesTristate(el, getValue, setValue) {
- const states = ["free", "scale", "chord"];
- cycleOnChange(el, states, getValue, (v) => {
- setValue(v);
- el.checked = v === "chord";
- el.indeterminate = v === "scale";
- });
- }
- function setupFeedbackTristates(formEl, triData) {
- const states = ["neutral", "negative", "positive"];
- for (const fieldset of formEl.querySelectorAll("fieldset[data-tri-group]")) {
- const group = fieldset.dataset.triGroup;
- triData[group] = {};
- const inputs = fieldset.querySelectorAll("input[type=checkbox][data-tri]");
- for (const input of inputs) {
- const name = input.name;
- triData[group][name] = states[0];
- cycleOnChange(
- input,
- states,
- () => triData[group][name],
- (v) => {
- triData[group][name] = v;
- applyFeedbackVisual(input, v);
- },
- );
- }
- }
- }
- function cycleOnChange(target, states, getValue, applyValue) {
- applyValue(getValue());
- target.addEventListener("change", () => {
- applyValue(states[(states.indexOf(getValue()) + 1) % states.length]);
- });
- }
- function applyFeedbackVisual(el, v) {
- el.checked = v === "positive";
- el.indeterminate = v === "negative";
- el.classList.remove("tristate-positive", "tristate-negative");
- if (v === "neutral") {
- el.removeAttribute("aria-invalid");
- return;
- }
- el.setAttribute("aria-invalid", v === "negative" ? "true" : "false");
- el.classList.add(`tristate-${v}`);
- }