From 314a84382ecb31a2f1faa879855ce20b9f442036 Mon Sep 17 00:00:00 2001 From: Brian Cully Date: Mon, 12 May 2025 22:29:56 -0400 Subject: wip: making some progress on the grid - got radio buttons back, but open/selected still have different interfaces. should use getAttribute() everywhere --- string.mjs | 155 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 155 insertions(+) create mode 100644 string.mjs (limited to 'string.mjs') diff --git a/string.mjs b/string.mjs new file mode 100644 index 0000000..4e1f198 --- /dev/null +++ b/string.mjs @@ -0,0 +1,155 @@ +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'); + }) + } +} -- cgit v1.3