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 ++++++++++++++++++++++++++++------------------------------ index.html | 100 ++++------------------------------ main.css | 22 ++++++++ main.mjs | 17 ++---- scale.mjs | 2 +- 5 files changed, 123 insertions(+), 190 deletions(-) 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; + } + } } diff --git a/index.html b/index.html index 72fd0f5..52c6ca8 100644 --- a/index.html +++ b/index.html @@ -8,95 +8,19 @@ - -
- - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
open••selected
E
A
D
G
B
E
- - -
+
diff --git a/main.css b/main.css index a2b4deb..f739fba 100644 --- a/main.css +++ b/main.css @@ -38,6 +38,24 @@ body { background-color: var(--fret-bg-color-3); } +x-string { + display: grid; + padding: 5px; + cursor: pointer; + list-style: none; + grid-template-rows: 1ex; + /* TODO: this should be put on the element when it's attached. */ + grid-template-columns: repeat(10, 1fr); +} + +x-string li { + text-align: center; +} + +x-string .selected { + border-left: 1px solid black; +} + x-fretboard { display: block; float: left; @@ -45,6 +63,10 @@ x-fretboard { margin-right: 1em; } +x-fretboard .save { + position: right; +} + x-fretboard table { border-collapse: collapse; } diff --git a/main.mjs b/main.mjs index 5f49097..4108f5d 100644 --- a/main.mjs +++ b/main.mjs @@ -1,20 +1,15 @@ -import Fretboard, { fretToNote } from './fretboard.mjs'; -import KeyPicker from './key-picker.mjs'; +import Fretboard from './fretboard.mjs'; import History from './history.mjs'; - -function notes(form) { - const strings = ['string1', 'string2', 'string3', 'string4', 'string5', 'string6']; - const formData = new FormData(form); - console.debug('notes', form, formData); - return strings.map((klass) => fretToNote(form, klass, formData.get(klass))); -} +import KeyPicker from './key-picker.mjs'; +import String from './string.mjs'; function init() { console.debug('init()', this); + String.register(); Fretboard.register(); - KeyPicker.register(); - History.register(); + // KeyPicker.register(); + // History.register(); // todo: maybe just attach the listener to document? document.querySelectorAll(Fretboard.name).forEach(f => { diff --git a/scale.mjs b/scale.mjs index b364a3e..5b04831 100644 --- a/scale.mjs +++ b/scale.mjs @@ -103,7 +103,7 @@ const ringHandler = { } }; -const chromaticScale = new Proxy( +export const chromaticScale = new Proxy( ['C', 'C♯', 'D', 'D♯', 'E', 'F', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B'], ringHandler); -- cgit v1.3