aboutsummaryrefslogtreecommitdiffstats
path: root/app/app.js
diff options
context:
space:
mode:
authors-ol <s-ol@users.noreply.github.com>2020-10-04 12:19:25 +0000
committers-ol <s-ol@users.noreply.github.com>2020-10-04 12:19:25 +0000
commit1ab9ed5b297048c5d3bc0278eddeb5a1d1c80367 (patch)
tree929cd8dd9eaa99894e4f7da9b2ad0f6e5be1abd6 /app/app.js
downloadisomorphic-kb-explorer-1ab9ed5b297048c5d3bc0278eddeb5a1d1c80367.tar.gz
isomorphic-kb-explorer-1ab9ed5b297048c5d3bc0278eddeb5a1d1c80367.zip
initial commit
Diffstat (limited to 'app/app.js')
-rw-r--r--app/app.js155
1 files changed, 155 insertions, 0 deletions
diff --git a/app/app.js b/app/app.js
new file mode 100644
index 0000000..27c0e38
--- /dev/null
+++ b/app/app.js
@@ -0,0 +1,155 @@
+import React from 'react';
+import css from './css';
+
+const statbyte = (stat, chan) => (stat << 4) | chan;
+const noteon = (key, vel=127, chan=0) => [statbyte(0b1001, chan), key, vel];
+const noteoff = (key, vel=127, chan=0) => [statbyte(0b1000, chan), key, vel];
+const ccchange = (ctl, val, chan=0) => [statbyte(0b1011, chan), ctl, val];
+
+const clamp = (val, min=0, max=1) => Math.max(min, Math.min(val, max));
+const ramp = i => new Array(i).fill(true).map((_, i) => i);
+const mix = (i, a, b) => i * a + (1-i) * b;
+
+window.ccchange = ccchange;
+window.noteon = noteon;
+
+const parse = message => ({
+ cmd: message.data[0] >> 4,
+ chan: message.data[0] & 0xf,
+ note: message.data[1],
+ val: message.data[2] / 127,
+});
+
+const longSize = 3;
+const rowStaggered = true;
+const w = longSize * (rowStaggered ? Math.sqrt(3) : 2 );
+const h = longSize * (rowStaggered ? 2 : Math.sqrt(3));
+const sx = longSize * (rowStaggered ? Math.sqrt(3) : 1.5 );
+const sy = longSize * (rowStaggered ? 1.5 : Math.sqrt(3));
+const rem = (n) => `${n}rem`;
+
+css(`
+body {
+ background: #212121;
+ margin: 2rem;
+
+ font-family: sans-serif;
+}
+
+.app {
+ position: relative;
+}
+
+.hexagon {
+ position: absolute;
+ width: ${rem(w)};
+ height: ${rem(h)};
+}
+
+.hexagon > .inner {
+ margin: auto;
+ width: ${rem(Math.min(w, h) * 0.8)};
+ height: ${rem(Math.min(w, h) * 0.8)};
+ padding: ${rem(longSize * 0.4)};
+ border-radius: ${rem(longSize)};
+ box-sizing: border-box;
+ font-size: ${rem(longSize * 0.5)};
+ text-align: center;
+ background: #696969;
+ color: #eeeeee;
+ cursor: pointer;
+
+ transition: background 300ms;
+}
+.hexagon > .inner:hover {
+ background: #848484;
+}
+.hexagon > .inner:active,
+.hexagon.active > .inner {
+ background: #eeeeee;
+ color: #696969;
+}
+`);
+
+const Hexagon = ({ x, y, children, state }) => {
+ if (rowStaggered) {
+ if (y % 2 == 0)
+ x += 0.5;
+ } else {
+ if (x % 2 == 0)
+ y += 0.5;
+ }
+
+ return (
+ <div
+ className={'hexagon' + (state ? ' active' : '')}
+ style={{
+ top: rem(y * sy),
+ left: rem(x * sx),
+ }}
+ >
+ <div
+ className="inner"
+ onClick={() => console.info(x,y)}
+ >
+ {children}
+ </div>
+ </div>
+ );
+};
+
+
+const range = i => new Array(i).fill(true);
+
+export default class App extends React.Component {
+ state = {};
+
+ componentDidUpdate(prevProps) {
+ if (prevProps.midiin)
+ prevProps.midiin.onmidimessage = null;
+
+ if (this.props.midiin)
+ this.props.midiin.onmidimessage = this.onmessage;
+ }
+
+ onmessage = (e) => {
+ const message = parse(e);
+
+ switch (message.cmd) {
+ case 9:
+ // NOTE_ON
+ this.setState(old => ({ ...old, [message.note]: true }));
+ break;
+
+ case 8:
+ // NOTE_OFF
+ this.setState(old => ({ ...old, [message.note]: false }));
+ break;
+
+ case 11:
+ // CC
+ break;
+ }
+ }
+
+ render() {
+ const xs = 10;
+ const ys = 4;
+
+ return (
+ <div className="app">
+ {range(xs).map((_, x) => (
+ range(ys).map((_, y) => {
+ let note = 36 + 2 * x + 6 * (ys - y);
+ if (y % 2 == 0) note += 1;
+ return (
+ <Hexagon key={x + ',' + y} x={x} y={y} note={note} state={this.state[note]}>
+ {note}
+ </Hexagon>
+ );
+ })
+ ))}
+ </div>
+ );
+ }
+};