import { chromaticScale } from "./scale.mjs"; function dots(i) { switch (i+1) { case 3: case 7: case 9: return '•' case 5: case 12: return '••' } } /* this.querySelectorAll('.open').forEach(elt => { elt.onclick = this.openClicked.bind(this); }); this.querySelectorAll('input[type="radio"]').forEach(elt => { elt.onmousedown = this.fretMousedown.bind(this); elt.onclick = this.fretClicked.bind(this); }); #mousedownVal; fretMousedown(e) { // at mousedown time, the form hasn't yet been changed. to support // deselecting elements, we need to know what the previous // selection was, so store it here. // // it'd be nice to be able to do this just in the click handler. this.#mousedownVal = e.target.checked; } openClicked(e) { const elt = e.target.parentNode; if (elt.classList.contains('muted')) { elt.classList.remove('muted'); elt.querySelectorAll('input[type="radio"]').forEach(input => { input.disabled = false; }) } else { elt.classList.add('muted'); elt.querySelectorAll('input[type="radio"]').forEach(input => { input.checked = false; input.disabled = true; }) } this.formChanged(); } fretClicked(e) { const elt = e.target; // if this element was selected at mousedown time, deselect it // now. if (this.#mousedownVal) { elt.checked = false; // doesn't get called automatically. this.formChanged(); } } */ export default class extends HTMLElement { // TODO: probably not worth observing frets since everything just // gets re-rendered when they change. static observedAttributes = ['frets', 'tonic', 'selected']; static name = 'x-string'; static register() { console.debug('Registering Element', this.name, this); customElements.define(this.name, this); } // TODO: this is a horrible hack, but i don't know how to make // each string's input unique. maybe wrap them in their own form? myName() { return 'string'; } attributeChangedCallback(name, old, v) { console.debug('String#attributeChangedCallback', this, name, old, v); if (old != v) { this.render(); } } connectedCallback() { console.debug('String#connectedCallback'); } zot() { console.debug('String#zot', this); const tmpl = this.querySelector('template'); console.log(' -- tmpl', tmpl); this.querySelectorAll('input[slot="fret"]').forEach(elt => { console.debug(' -- del', elt, parent); // TODO: this is precisely what i was trying to avoid: the // code has to know exactly what the template looks like. elt.parentNode.parentNode.removeChild(elt.parentNode); }) } muteClicked(e) { console.debug('String#muteClicked', this, e); if (this.getAttribute('selected') !== 'x') { this.setAttribute('selected', 'x'); this.classList.add('muted'); } else { this.setAttribute('selected', this.getAttribute('tonic')); this.classList.remove('muted'); } } fretClicked(e) { console.debug('String#fretClicked', this, e); console.debug(' -- note', e.target.note); this.setAttribute('selected', e.target.note); } render() { console.debug('String#render'); this.querySelectorAll('input[name="muted"]').forEach(elt => { console.debug(' -- input', elt); elt.onchange = this.muteClicked.bind(this); }); this.zot(); const tmpl = this.querySelector('template'); this.querySelectorAll('slot[name="open"]').forEach(s => { s.note = this.getAttribute('tonic'); s.innerText = s.note; s.parentNode.onclick = this.fretClicked.bind(this); }) const chromIndex = chromaticScale.indexOf(this.getAttribute('tonic')); const frets = Number(this.getAttribute('frets')); for (let i = 1; i <= frets; i++) { const item = tmpl.content.cloneNode(true); item.querySelectorAll('input[slot="fret"]').forEach(s => { s.setAttribute('name', this.myName()); s.setAttribute('value', chromaticScale[chromIndex + i]); s.parentNode.onclick = this.fretClicked.bind(this); }) tmpl.parentNode.insertBefore(item, tmpl); } this.querySelectorAll('slot[name="selected"]').forEach(s => { s.innerText = this.getAttribute('selected'); }) } }