change menu and start saving process
convert file to state wip finish file to state conversion load in file to state (needs to be fixed in order to properly load some things in) formatting Add microkorg sysex decoding Correctly parse bit array sections Add sysex encoding and header creation remove log Finish format file v1 Fix a definitions Move stuff around add file to store functions Finish prepare_timbre begin to Setup menu Gut a lot of stuff, and implement the menu Begin overhaul of fileupload process Finish hooking up the menu clean up start saving functions add unused state, mostly finish saveStore add self reminder complete store saving process 1 store to simplified store is complete
This commit is contained in:
@@ -1,247 +0,0 @@
|
||||
.menu,
|
||||
.menu-true,
|
||||
.menu-false {
|
||||
position: fixed;
|
||||
background: white;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 99;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
min-height: 100vh;
|
||||
min-width: 100vw;
|
||||
}
|
||||
|
||||
.menu-false {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.menu-true-animation {
|
||||
-webkit-animation: scale-in-out-tl 0.3s
|
||||
cubic-bezier(0.55, 0.085, 0.68, 0.53) both;
|
||||
animation: scale-in-out-tl 0.3s cubic-bezier(0.55, 0.085, 0.68, 0.53) both;
|
||||
}
|
||||
|
||||
.menu-false-animation {
|
||||
-webkit-animation: scale-in-out-tl 0.3s
|
||||
cubic-bezier(0.55, 0.085, 0.68, 0.53) reverse both;
|
||||
animation: scale-in-out-tl 0.3s cubic-bezier(0.55, 0.085, 0.68, 0.53)
|
||||
reverse both;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------
|
||||
* Generated by Animista on 2020-11-21 14:21:54
|
||||
* Licensed under FreeBSD License.
|
||||
* See http://animista.net/license for more info.
|
||||
* w: http://animista.net, t: @cssanimista
|
||||
* ---------------------------------------------- */
|
||||
|
||||
/**
|
||||
* ----------------------------------------
|
||||
* animation scale-out-tl
|
||||
* ----------------------------------------
|
||||
*/
|
||||
@-webkit-keyframes scale-in-out-tl {
|
||||
0% {
|
||||
-webkit-transform: scale(1);
|
||||
transform: scale(1);
|
||||
clip-path: circle(100%);
|
||||
-webkit-transform-origin: 0 0;
|
||||
transform-origin: 0 0;
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
-webkit-transform: scale(0);
|
||||
transform: scale(0);
|
||||
clip-path: circle(0%);
|
||||
-webkit-transform-origin: 0 0;
|
||||
transform-origin: 0 0;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
@keyframes scale-in-out-tl {
|
||||
0% {
|
||||
-webkit-transform: scale(1);
|
||||
transform: scale(1);
|
||||
-webkit-transform-origin: 0 0;
|
||||
clip-path: circle(100%);
|
||||
transform-origin: 0 0;
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
-webkit-transform: scale(0);
|
||||
transform: scale(0);
|
||||
clip-path: circle(0%);
|
||||
-webkit-transform-origin: 0 0;
|
||||
transform-origin: 0 0;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.menu-logo {
|
||||
height: 40vmin;
|
||||
pointer-events: none;
|
||||
padding: 0.5em;
|
||||
-webkit-user-drag: none;
|
||||
-khtml-user-drag: none;
|
||||
-moz-user-drag: none;
|
||||
-o-user-drag: none;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-o-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.menu-title {
|
||||
color: #1c1d21;
|
||||
font-family: 'Lato', sans-serif;
|
||||
line-height: 1em;
|
||||
margin: 0 0 0.5em 0;
|
||||
}
|
||||
h1 {
|
||||
margin: 0.25em;
|
||||
font-weight: 700;
|
||||
line-height: 0.85em;
|
||||
font-size: 2.5em;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin: 0.25em;
|
||||
font-weight: 500;
|
||||
line-height: 1em;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.menu-button,
|
||||
.menu-open-button {
|
||||
background: transparent;
|
||||
display: inline-block;
|
||||
margin: 1em;
|
||||
padding: 0.35em 1em;
|
||||
border: 0.1em solid slateblue;
|
||||
border-radius: 0.12em;
|
||||
color: slateblue;
|
||||
font-family: 'Roboto', sans-serif;
|
||||
font-size: 1em;
|
||||
font-weight: 300;
|
||||
text-align: center;
|
||||
outline: none;
|
||||
transition: all 0.2s ease-in-out;
|
||||
-webkit-touch-callout: none; /* iOS Safari */
|
||||
-webkit-user-select: none; /* Safari */
|
||||
-khtml-user-select: none; /* Konqueror HTML */
|
||||
-moz-user-select: none; /* Old versions of Firefox */
|
||||
-ms-user-select: none; /* Internet Explorer/Edge */
|
||||
user-select: none;
|
||||
}
|
||||
.menu-button:hover {
|
||||
color: white;
|
||||
background: slateblue;
|
||||
}
|
||||
|
||||
.menu-open-button:hover {
|
||||
color: white;
|
||||
background: #ef476f;
|
||||
}
|
||||
|
||||
.menu-open-button {
|
||||
z-index: 100;
|
||||
height: 1.495em;
|
||||
line-height: 0.9em;
|
||||
position: absolute;
|
||||
padding: 0em 0.3em;
|
||||
color: #ef476f;
|
||||
border: 0.09em solid #ef476f;
|
||||
margin: 0;
|
||||
top: 0.55em;
|
||||
left: 0.5em;
|
||||
}
|
||||
|
||||
/* change how the button is handled when screen is smaller then 880px*/
|
||||
|
||||
.menu-open-button::before {
|
||||
content: '\2630';
|
||||
}
|
||||
|
||||
.modal {
|
||||
margin: auto;
|
||||
height: 8em;
|
||||
width: 14em;
|
||||
position: fixed;
|
||||
z-index: 100;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.modal-hide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.modal-display {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.modal-main {
|
||||
background: white;
|
||||
padding: 0.75em 0 1em;
|
||||
border: solid 0.09em slategrey;
|
||||
border-radius: 6px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
font-size: 0.75em;
|
||||
line-height: 1.25em;
|
||||
text-align: left;
|
||||
margin: 0em 9em 0 0;
|
||||
}
|
||||
|
||||
.modal-text {
|
||||
flex-grow: 3;
|
||||
margin: 0.5em 0em 1em 1.85em;
|
||||
text-align: left;
|
||||
line-height: 1.5em;
|
||||
font-size: 0.65em;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.button-save,
|
||||
.button-no-save,
|
||||
.button-cancel {
|
||||
width: 25%;
|
||||
font-size: 0.5em;
|
||||
margin: 0 0.5em;
|
||||
border-radius: 0.2em;
|
||||
padding: 0.35em 0;
|
||||
}
|
||||
|
||||
.button-save {
|
||||
color: mediumseagreen;
|
||||
border-color: mediumseagreen;
|
||||
}
|
||||
.button-save:hover {
|
||||
background: mediumseagreen;
|
||||
}
|
||||
.button-no-save {
|
||||
color: crimson;
|
||||
border-color: crimson;
|
||||
}
|
||||
.button-no-save:hover {
|
||||
background: crimson;
|
||||
}
|
||||
.button-cancel {
|
||||
color: slategray;
|
||||
border-color: grey;
|
||||
}
|
||||
.button-cancel:hover {
|
||||
background: slategray;
|
||||
}
|
||||
@@ -1,146 +0,0 @@
|
||||
import React, { useState, useRef, useEffect } from 'react';
|
||||
import IO from '../io/IO';
|
||||
import { useDispatch, useSelector } from 'react-redux'; // probably temporarily here
|
||||
import { parameterRefreshAll } from '../timbre/parameters/parameterSlice'; // probably temporarily here
|
||||
import logo from '../../1f3b9.svg';
|
||||
import './Menu.css';
|
||||
|
||||
// for things such as (do you want to save? or do you want to reset things?)
|
||||
const Modal = ({ children, show, setShow }) => {
|
||||
const node = useRef();
|
||||
const modalDisplay = show ? 'modal modal-display' : 'modal modal-hide';
|
||||
|
||||
useEffect(() => {
|
||||
const current = node.current;
|
||||
|
||||
const handleOutsideClick = (event) => {
|
||||
console.log('clicking anywhere');
|
||||
if (current.contains(event.target)) {
|
||||
return;
|
||||
} else setShow();
|
||||
};
|
||||
|
||||
if (show) {
|
||||
document.addEventListener('mousedown', handleOutsideClick);
|
||||
} else {
|
||||
document.removeEventListener('mousedown', handleOutsideClick);
|
||||
}
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('mousedown', handleOutsideClick);
|
||||
};
|
||||
}, [show, setShow]);
|
||||
|
||||
return (
|
||||
<div ref={node} className={modalDisplay}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default function MainMenu() {
|
||||
const unsaved = useSelector((state) => state.parameters.unsaved);
|
||||
const [open, setOpen] = useState({
|
||||
className: '',
|
||||
openState: true,
|
||||
});
|
||||
const [showModal, setShowModal] = useState({
|
||||
saveCheck: false,
|
||||
ioModal: false,
|
||||
});
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const HandleClick = () => {
|
||||
setOpen({
|
||||
className: open.openState
|
||||
? 'menu-true-animation'
|
||||
: 'menu-false-animation',
|
||||
openState: !open.openState,
|
||||
});
|
||||
};
|
||||
|
||||
const AnimationEnd = () => {
|
||||
setOpen({
|
||||
...open,
|
||||
className: `menu-${open.openState}`,
|
||||
});
|
||||
};
|
||||
|
||||
const showSaveCheck = () => {
|
||||
setShowModal({ ...showModal, saveCheck: !showModal.saveCheck });
|
||||
};
|
||||
|
||||
const showIOModal = () => {
|
||||
setShowModal({ ...showModal, ioModal: !showModal.ioModal });
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Modal show={showModal.saveCheck} setShow={showSaveCheck}>
|
||||
<div className="modal-main">
|
||||
<span className="modal-header">Save Changes</span>
|
||||
<span className="modal-text">
|
||||
Do you want to save your changes?
|
||||
</span>
|
||||
<button
|
||||
className="menu-button button-save"
|
||||
onClick={() => {
|
||||
showSaveCheck();
|
||||
HandleClick();
|
||||
}} // temporary while I implement the binary parsing stuff
|
||||
>
|
||||
Save Changes
|
||||
</button>
|
||||
<button
|
||||
className="menu-button button-no-save"
|
||||
onClick={() => {
|
||||
dispatch(parameterRefreshAll());
|
||||
showSaveCheck();
|
||||
HandleClick();
|
||||
}}>
|
||||
Don't Save
|
||||
</button>
|
||||
<button
|
||||
className="menu-button button-cancel"
|
||||
onClick={showSaveCheck}>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</Modal>
|
||||
<div
|
||||
className={`menu ${open.className}`}
|
||||
onAnimationEnd={AnimationEnd}>
|
||||
<img
|
||||
src={logo}
|
||||
className="menu-logo"
|
||||
alt="logo"
|
||||
draggable={false}
|
||||
/>
|
||||
<header className="menu-title">
|
||||
<h1>Alapatch</h1>
|
||||
<h2>A microKORG Patch Editor</h2>
|
||||
</header>
|
||||
<div className="menu-button-container">
|
||||
<button
|
||||
className="menu-button"
|
||||
onClick={() => {
|
||||
!unsaved ? HandleClick() : showSaveCheck();
|
||||
}}>
|
||||
New Patch
|
||||
</button>
|
||||
<button
|
||||
className="menu-button"
|
||||
onClick={() => {
|
||||
!unsaved ? showIOModal() : showSaveCheck();
|
||||
}}>
|
||||
Load Patch
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<Modal show={showModal.ioModal} setShow={showIOModal}>
|
||||
<IO />
|
||||
</Modal>
|
||||
<button className="menu-open-button" onClick={HandleClick} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
import { useRef, useState, useEffect } from 'react';
|
||||
import './menu.css';
|
||||
import { SelectList } from '../helpers/Helpers';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import {
|
||||
parameterUpdateIndex,
|
||||
parameterUpdated,
|
||||
parameterFromFile,
|
||||
parameterRefreshAll,
|
||||
selectEntities,
|
||||
} from '../timbre/parameters/parameterSlice'; // probably temporarily here
|
||||
import { return_store_from_file, return_file_from_store } from '../io';
|
||||
|
||||
export default function Menu() {
|
||||
const [file, setFile] = useState(null);
|
||||
const fileRef = useRef(null);
|
||||
const state = useSelector((state) => selectEntities(state));
|
||||
const dispatch = useDispatch();
|
||||
const reader = new FileReader();
|
||||
const loadFile = (f) => {
|
||||
reader.onloadend = (e) => {
|
||||
setFile(new Uint8Array(e.target.result));
|
||||
};
|
||||
reader.readAsArrayBuffer(f);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (file) {
|
||||
dispatch(parameterFromFile(return_store_from_file(file)));
|
||||
}
|
||||
}, [file, dispatch]);
|
||||
|
||||
return (
|
||||
<div className='menu'>
|
||||
<div className='menu-patch-container'>
|
||||
<div className='patch-name'>
|
||||
<input
|
||||
placeholder='Patch Name'
|
||||
type='text'
|
||||
maxLength='12'
|
||||
value={state.patch.name}
|
||||
onChange={(e) => {
|
||||
dispatch(
|
||||
parameterUpdated({
|
||||
id: 'Patch',
|
||||
changes: { name: e.target.value },
|
||||
})
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className='patch-load'>
|
||||
<button
|
||||
className='menu-button'
|
||||
onClick={() => {
|
||||
dispatch(parameterRefreshAll());
|
||||
}}>
|
||||
New Patch
|
||||
</button>
|
||||
<button
|
||||
className='menu-button'
|
||||
onClick={() => {
|
||||
fileRef.current.click();
|
||||
}}>
|
||||
Load Patch
|
||||
</button>
|
||||
<button
|
||||
className='menu-button'
|
||||
onClick={() => {
|
||||
return_file_from_store(state);
|
||||
}}>
|
||||
Save Patch
|
||||
</button>
|
||||
<input
|
||||
type='file'
|
||||
accept='.syx'
|
||||
className='menu-file-input'
|
||||
ref={fileRef}
|
||||
onChange={(e) => {
|
||||
loadFile(e.target.files[0]);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className='patch-settings'>
|
||||
<SelectList
|
||||
value={state.patch.mode}
|
||||
list={[
|
||||
{ value: 'Single' },
|
||||
{ value: 'Multiple' },
|
||||
{ value: 'Vocoder' },
|
||||
]}
|
||||
onChange={(value) => {
|
||||
dispatch(
|
||||
parameterUpdated({
|
||||
id: 'patch',
|
||||
changes: { mode: value },
|
||||
})
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className='menu-timbre-select'>
|
||||
<button
|
||||
disabled={state.patch.mode === 'Vocoder'}
|
||||
className='menu-button'
|
||||
onClick={() => {
|
||||
dispatch(parameterUpdateIndex(0));
|
||||
}}>
|
||||
Timbre 1
|
||||
</button>
|
||||
<button
|
||||
disabled={state.patch.mode !== 'Multiple'}
|
||||
className='menu-button'
|
||||
onClick={() => {
|
||||
dispatch(parameterUpdateIndex(1));
|
||||
}}>
|
||||
Timbre 2
|
||||
</button>
|
||||
<button
|
||||
disabled={state.patch.mode !== 'Vocoder'}
|
||||
className='menu-button'
|
||||
onClick={() => {
|
||||
dispatch(parameterUpdateIndex(2));
|
||||
}}>
|
||||
Vocoder
|
||||
</button>
|
||||
<button
|
||||
className='menu-button'
|
||||
onClick={() => {
|
||||
dispatch(parameterUpdateIndex(3));
|
||||
}}>
|
||||
Effects
|
||||
</button>
|
||||
<button
|
||||
className='menu-button'
|
||||
onClick={() => {
|
||||
dispatch(parameterUpdateIndex(4));
|
||||
}}>
|
||||
Arpeggio
|
||||
</button>
|
||||
</div>
|
||||
<div className='menu-title'>
|
||||
Alapatch <br /> A microKORG Patch Editor
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
.menu {
|
||||
height: 100vh;
|
||||
min-width: 15vw;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-wrap: nowrap;
|
||||
justify-content: space-around;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.menu-patch-container > * {
|
||||
margin-top: 10pt;
|
||||
}
|
||||
|
||||
input[type='text'] {
|
||||
font-family: 'Roboto', sans-serif;
|
||||
text-indent: 8pt;
|
||||
width: 70%;
|
||||
padding: 6pt 0;
|
||||
outline: none;
|
||||
border-radius: 4pt;
|
||||
border: 1px solid gainsboro;
|
||||
}
|
||||
input[type='text']:hover {
|
||||
border-color: silver;
|
||||
}
|
||||
input[type='text']:focus {
|
||||
border: 2px solid dodgerblue;
|
||||
}
|
||||
|
||||
.menu-button {
|
||||
width: 65%;
|
||||
display: block;
|
||||
padding: 4pt;
|
||||
margin: 8px auto;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
h2 {
|
||||
text-align: left;
|
||||
font-size: x-large;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.menu-file-input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.patch-settings {
|
||||
font-size: small;
|
||||
width: 75%;
|
||||
margin: 10pt auto;
|
||||
line-height: 1.5em;
|
||||
}
|
||||
Reference in New Issue
Block a user