git.s-ol.nu isomorphic-kb-explorer / 8ce1d7f
toolbar, focus, editable size s-ol 1 year, 9 days ago
2 changed file(s) with 138 addition(s) and 56 deletion(s). Raw diff Collapse all Expand all
00 import React from 'react';
1 import { pos, width, height } from './style';
1 import { css, pos, width, height } from './style';
22 import * as notes from './notes';
33
44 const statbyte = (stat, chan) => (stat << 4) | chan;
155155 );
156156 };
157157
158 css(`
159 nav {
160 display: flex;
161 flex-wrap: wrap;
162 justfiy-content: space-between;
163 }
164
165 nav > .group, nav > .spacer {
166 display: flex;
167 align-items: baseline;
168 padding: 0.25em 1em;
169 gap: 1em;
170
171 border: 0 solid #363636;
172 border-width: 1px 0 1px 0;
173 }
174
175 nav > .group:nth-child(2n) {
176 background: #363636;
177 }
178
179 nav > .spacer { flex: 1; }
180 `);
181
182 css(`
183 main {
184 position: relative;
185 padding: 0.5em;
186 margin: 1em 0;
187
188 display: flex;
189 justify-content: space-evenly;
190 }
191
192 main .focus-msg {
193 position: absolute;
194 inset: 0;
195
196 display: flex;
197 font-size: 5em;
198 justify-content: space-around;
199 align-items: center;
200 text-align: center;
201
202 border: 4px solid white;
203 background: #212121;
204 pointer-events: none;
205
206 opacity: 0.8;
207 transition: opacity 0.4s;
208 }
209
210 main:focus-within .focus-msg {
211 opacity: 0;
212 }
213 `);
214
158215 export default class App extends React.Component {
159216 state = {
160217 layout: 'wicki_hayden',
161218 scale: 'major',
162219 offset: 60,
163220 state: {},
221 w: 12,
222 h: 4,
164223 };
165224
225 ref = React.createRef();
226
166227 componentDidMount() {
167 document.addEventListener("keydown", this.onkeydown);
168 document.addEventListener("keyup", this.onkeyup);
169 }
170
171 componentWillUnmount() {
172 document.removeEventListener("keydown", this.onkeydown);
173 document.removeEventListener("keyup", this.onkeyup);
228 this.ref.current.addEventListener("keydown", this.onkeydown);
229 this.ref.current.addEventListener("keyup", this.onkeyup);
174230 }
175231
176232 componentDidUpdate(prevProps) {
213269 this.send(NOTE_OFF, note);
214270 }
215271
216 onlayout = (e) => this.setState({ layout: e.target.value })
217 onscale = (e) => this.setState({ scale: e.target.value })
218
219 onoffset = (e) => this.setState({ offset: null });
272 set(key) {
273 return (e) => {
274 let value = e.target.value;
275 if (key === 'w' || key === 'h') value = +value;
276 this.setState({ [key]: value });
277 };
278 }
279
280 onoffset = (offset) => (e) => this.setState({ offset });
220281
221282 onmessage = (e) => {
222283 const message = parse(e);
258319
259320 render() {
260321 const { configure, showMidi } = this.props;
261 const { scale, offset, layout, state } = this.state;
322 const { w, h, scale, offset, layout, state } = this.state;
262323
263324 const chord = Object.entries(state).filter(([note, on]) => on).map(([k, _]) => k);
264325 chord.sort();
265326
266327 return (
267328 <div className="app">
268 <button onClick={configure}>midi settings</button>
269 <div className="option">
270 <label>layout:</label>
271 <select name="layout" onChange={this.onlayout} value={layout}>
272 <option value="wicki_hayden">Wicki-Hayden</option>
273 <option value="harmonic">Harmonic Table</option>
274 <option value="gerhard">Gerhard</option>
275 </select>
276 </div>
277 <div className="option">
278 <label>scale:</label>
279 <select name="layout" onChange={this.onscale} value={scale}>
280 <option value="none">None</option>
281 <option value="major">Major</option>
282 <option value="minor_nat">Natural Minor</option>
283 <option value="minor_harm">Harmonic Minor</option>
284 <option value="minor_mel">Melodic Minor</option>
285 <option value="minor_hung">Hungarian Minor</option>
286 <option value="whole">Whole-Tone</option>
287 <option value="penta">Pentatonic</option>
288 </select>
289 <button onClick={this.onoffset}>set offset</button>
290 </div>
291
292 <div className="main">
329 <nav>
330 <div className="group">
331 <button onClick={configure}>midi settings</button>
332 </div>
333 <div className="group">
334 <label>layout:</label>
335 <select name="layout" value={layout} onChange={this.set('layout')}>
336 <option value="wicki_hayden">Wicki-Hayden</option>
337 <option value="harmonic">Harmonic Table</option>
338 <option value="gerhard">Gerhard</option>
339 </select>
340 </div>
341 <div className="group">
342 <label>scale:</label>
343 <button onClick={this.onoffset(null)}>
344 {notes.music[offset % 12]}
345 </button>
346 <select name="layout" value={scale} onChange={this.set('scale')}>
347 <option value="none">None</option>
348 <option value="major">Major</option>
349 <option value="minor_nat">Natural Minor</option>
350 <option value="minor_harm">Harmonic Minor</option>
351 <option value="minor_mel">Melodic Minor</option>
352 <option value="minor_hung">Hungarian Minor</option>
353 <option value="whole">Whole-Tone</option>
354 <option value="penta">Pentatonic</option>
355 </select>
356 </div>
357 <div className="group">
358 <label>octave:</label>
359 <button onClick={this.onoffset(offset - 12)}>-</button>
360 {Math.floor(offset / 12)}
361 <button onClick={this.onoffset(offset + 12)}>+</button>
362 </div>
363 <div className="group">
364 <label>size:</label>
365 <input className="small" type="number" min="1" value={w} onChange={this.set('w')} />
366 {'x'}
367 <input className="small" type="number" min="1" value={h} onChange={this.set('h')} />
368 </div>
369 <div className="spacer" />
370 </nav>
371
372 <main ref={this.ref} tabIndex="0">
293373 <Keyboard
294 w={12}
295 h={4}
374 w={w}
375 h={h}
296376 noteon={this.noteon}
297377 noteoff={this.noteoff}
298378 showMidi={showMidi}
301381 offset={offset}
302382 state={state}
303383 />
304 <ChordView
305 chord={chord}
306 />
307 </div>
384 <ChordView chord={chord} />
385 <div className="focus-msg">
386 <span>click here to activate<br/>keyboard input</span>
387 </div>
388 </main>
308389 </div>
309390 );
310391 }
5656 border: 1px solid #eeeeee;
5757 color: #eeeeee;
5858 padding: 0.25em;
59 margin: 0.125em 0;
6059 border-radius: 0.5em;
60 }
61
62 input.small {
63 width: 4em;
64 }
65
66 button {
67 padding: 0.25em 0.5em;
68 }
69
70 label {
71 display: inline-block;
72 font-weight: bold;
6173 }
6274
6375 .app, .keyboard, .chord {
6476 position: relative;
65 }
66
67 .main {
68 display: flex;
69 justify-content: space-evenly;
70 }
71
72 .option label {
73 display: inline-block;
74 width: 4em;
75 margin-right: 1em;
7677 }
7778
7879 .hexagon {