From ceff45422d39530b09d3d797ef88b97190c4f23c Mon Sep 17 00:00:00 2001 From: Brian Cully Date: Mon, 12 May 2025 21:52:00 -0400 Subject: wip: major refactor for web components and grid layout - hovering on notes from the key doesn't work. - need to show fret on/off, not notes, they're too distracting - save button broken --- fretboard.mjs | 172 ++++++++++++++++++++++++++++------------------------------ 1 file changed, 82 insertions(+), 90 deletions(-) (limited to 'fretboard.mjs') diff --git a/fretboard.mjs b/fretboard.mjs index ef9e0b5..7ba0dab 100644 --- a/fretboard.mjs +++ b/fretboard.mjs @@ -1,22 +1,14 @@ -import { Note } from "./scale.mjs"; - // open string notes, starting from the deepest string. -const strings = { - string1: 'E', - string2: 'A', - string3: 'D', - string4: 'G', - string5: 'B', - string6: 'E' -}; +const openStrings = ['E', 'A', 'D', 'G', 'B', 'E']; // convert ‘string1’ ‘fret2’ in ‘form’ to F# export function fretToNote(form, stringName, fretName) { - const string = strings[stringName]; - if (!string) { - return null; + if (!form) { + console.error('fretToNote with no form!'); + return 'C'; } + console.log('lookingforfret', stringName, form, form.querySelector(`.${stringName} .open`)); if (form.querySelector(`.${stringName}.muted`)) { return 'x'; } else if (!fretName?.startsWith('fret')) { @@ -26,64 +18,9 @@ export function fretToNote(form, stringName, fretName) { } } -function formChanged(form) { - const formData = new FormData(form); - form.querySelectorAll('tbody .selected').forEach(elt => { - const string = Array.from(elt.parentNode.classList).filter(x => x.startsWith('string'))[0]; - const val = formData.get(string); - const note = Note.fromString(fretToNote(form, string, val)); - if (note.isSharp) { - elt.innerText = `${note} / ${note.toAlternateString()}`; - } else { - elt.innerText = note; - } - }); -} - -function handleFormChanged(e) { - formChanged(e.target.form); -} - -let mousedownVal = undefined; - -function 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. - mousedownVal = e.target.checked; -} - -function fretClick(e) { - const elt = e.target; - // if this element was selected at mousedown time, deselect it - // now. - if (mousedownVal) { - elt.checked = false; - // doesn't get called automatically. - formChanged(elt.form); - } -} - -function openClick(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; - }) - } - formChanged(elt.closest('form')); -} - export default class extends HTMLElement { + static observedAttributes = ['strings', 'frets']; + static name = 'x-fretboard'; static register() { console.debug('Registering Element', this.name, this); @@ -98,22 +35,15 @@ export default class extends HTMLElement { console.debug('Fretboard#constructor', this); } + attributeChangedCallback(name, old, v) { + console.debug('Fretboard#attributeChangedCallback', + this, name, old, v); + this.formChanged(); + } + connectedCallback() { console.debug('Fretboard#connectedCallback', this); - - // TODO: don't trawl the dom, custom element means we know - // what's inside. - this.form = this.querySelector('form'); - this.form.onchange = handleFormChanged; - this.form.querySelectorAll('.open').forEach(elt => { - elt.onclick = openClick; - }); - this.form.querySelectorAll('input[type="radio"]').forEach(elt => { - elt.onmousedown = fretMousedown; - elt.onclick = fretClick; - }); - - this.form.querySelectorAll('.save').forEach(elt => { + this.querySelectorAll('.save').forEach(elt => { elt.onclick = (e) => { console.log('Fretboard#onSave'); e.preventDefault(); @@ -125,16 +55,78 @@ export default class extends HTMLElement { this.dispatchEvent(event); }; }) - formChanged(this.form); + this.formChanged(); } get notes() { console.debug('Fretboard#notes', this); - const formData = new FormData(this.form); - return Array.from(this.form.querySelectorAll('tbody .selected')).map(elt => { - const string = Array.from(elt.parentNode.classList).filter(x => x.startsWith('string'))[0]; - const val = formData.get(string); - return fretToNote(this.form, string, val); + return []; + // const formData = new FormData(this.form); + // return Array.from(this.form.querySelectorAll('tbody .selected')).map(elt => { + // const string = Array.from(elt.parentNode.classList).filter(x => x.startsWith('string'))[0]; + // const val = formData.get(string); + // return fretToNote(this.form, string, val); + // }) + } + + updateSelected(formData) { + // this.querySelectorAll('tbody .selected').forEach(elt => { + // const string = Array.from(elt.parentNode.classList).filter(x => x.startsWith('string'))[0]; + // const val = formData.get(string); + // const note = Note.fromString(fretToNote(this, string, val)); + // if (note.isSharp) { + // elt.innerText = `${note} / ${note.toAlternateString()}`; + // } else { + // elt.innerText = note; + // } + // }); + } + + zotStrings() { + console.debug('Fretboard#zotStrings', this); + const tmpl = this.querySelector('template.string'); + console.log(' -- tmpl', tmpl); + const parent = tmpl.parentNode; + parent.querySelectorAll('[slot="string"]').forEach(elt => { + console.debug(' -- del', elt); + parent.removeChild(elt); }) } + + zotForm() { + console.debug('Fretboard#zotForm', this); + this.zotStrings(); + } + + #strings; #frets; + formChanged() { + console.debug('Fretboard#formChanged', this); + // TODO: this is inelegant. ideally, none of this stuff is + // triggered until the form is set, and doing it here is not + // robust. + const [strings, frets] = + [this.getAttribute('strings'), this.getAttribute('frets')].map(s => Number(s)); + console.debug(' -- strings', strings, 'frets', frets); + if (strings && frets && strings !== this.#strings && frets != this.#frets) { + console.debug(' -- rerender table'); + this.zotForm(); + + const tmpl = this.querySelector('template.string'); + for (let i = 0; i < strings; i++) { + console.debug(' -- appending string', i, tmpl); + const item = tmpl.content.cloneNode(true); + console.debug(' -- item', item); + const tonic = openStrings[i]; + item.querySelectorAll('[slot="string"]').forEach(s => { + console.debug(' -- setting tonic', tonic, 'on', s); + s.setAttribute('tonic', tonic); + s.setAttribute('selected', tonic); + s.setAttribute('frets', frets.toString()); + }) + tmpl.parentNode.insertBefore(item, tmpl); + } + this.#frets = frets; + this.#strings = strings; + } + } } -- cgit v1.3