Skip to content

Commit

Permalink
[MOD] Migrate to Vite
Browse files Browse the repository at this point in the history
  • Loading branch information
oscarrc committed Jul 26, 2024
1 parent 5bbb266 commit 17ff36b
Show file tree
Hide file tree
Showing 33 changed files with 1,971 additions and 1,893 deletions.
12 changes: 0 additions & 12 deletions compress-cra.json

This file was deleted.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{
"name": "nts-web",
"description": "A web controller for the Korg NTS-1",
"version": "3.0.1",
"version": "4.0.0",
"type": "module",
"author": {
"name": "Oscar R.C.",
"email": "oscarrc.web@gmail.com",
Expand Down
3 changes: 3 additions & 0 deletions postcss.config.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
plugins: [require("tailwindcss"), require("autoprefixer")],
};
7 changes: 0 additions & 7 deletions postcss.config.js

This file was deleted.

File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,41 +1,41 @@
import { SlArrowDown } from "react-icons/sl";
import switchButton from "../../assets/switch.png";

const Dropdown = ({ id, label, value = 0, options, switchValue, isActive, onChange, onSwitch }) => {
const handleSelection = (i) => {
document.activeElement.blur()
onChange && onChange(i)
}

return (
<div className={`input-select flex flex-1 px-4 items-center gap-4 ${isNaN(switchValue) && 'py-1.5'}`}>
{ label && <label className="text-secondary text-xs uppercase font-bold" htmlFor={id}>{label}</label> }
{ !isNaN(switchValue) &&
<input type="checkbox"
{...(label ? {id: id} : {"aria-label": id}) }
name={label}
onChange={ (e) => onSwitch(e.target.checked) }
checked={ isActive || false }
className="input-switch"
data-diameter="60"
data-src={ switchButton }
/>
}
<div className="group dropdown flex-1 my-2">
<button type="button" tabIndex="0" className={`relative text-left cursor-pointer bg-grid px-2 py-1 w-full font-sevenSegment rounded text-accent outline outline-base-100 outline-offset-2 outline-1 hover:outline-accent focus:outline-accent`}>
{ options?.length ? options?.[value] : "No option" }
<SlArrowDown className="text-accent absolute top-2 right-2 h-4 w-4" />
</button>
<ul tabIndex="0" className="mt-2 dropdown-content menu shadow-lg bg-neutral text-secondary rounded w-full max-h-[8rem] flex-row overflow-y-scroll">
{
options?.map((option, index) => {
return <li key={ index } onClick={ () => handleSelection(index) } className="px-2 py-1 cursor-pointer hover:bg-base-100 w-full">{ option }</li>
})
}
</ul>
</div>
</div>
)
}

import { SlArrowDown } from "react-icons/sl";
import switchButton from "../../assets/switch.png";

const Dropdown = ({ id, label, value = 0, options, switchValue, isActive, onChange, onSwitch }) => {
const handleSelection = (i) => {
document.activeElement.blur()
onChange && onChange(i)
}

return (
<div className={`input-select flex flex-1 px-4 items-center gap-4 ${isNaN(switchValue) && 'py-1.5'}`}>
{ label && <label className="text-secondary text-xs uppercase font-bold" htmlFor={id}>{label}</label> }
{ !isNaN(switchValue) &&
<input type="checkbox"
{...(label ? {id: id} : {"aria-label": id}) }
name={label}
onChange={ (e) => onSwitch(e.target.checked) }
checked={ isActive || false }
className="input-switch"
data-diameter="60"
data-src={ switchButton }
/>
}
<div className="group dropdown flex-1 my-2">
<button type="button" tabIndex="0" className={`relative text-left cursor-pointer bg-grid px-2 py-1 w-full font-sevenSegment rounded text-accent outline outline-base-100 outline-offset-2 outline-1 hover:outline-accent focus:outline-accent`}>
{ options?.length ? options?.[value] : "No option" }
<SlArrowDown className="text-accent absolute top-2 right-2 h-4 w-4" />
</button>
<ul tabIndex="0" className="mt-2 dropdown-content menu shadow-lg bg-neutral text-secondary rounded w-full max-h-[8rem] flex-row overflow-y-scroll">
{
options?.map((option, index) => {
return <li key={ index } onClick={ () => handleSelection(index) } className="px-2 py-1 cursor-pointer hover:bg-base-100 w-full">{ option }</li>
})
}
</ul>
</div>
</div>
)
}

export default Dropdown;
Original file line number Diff line number Diff line change
@@ -1,82 +1,82 @@
import { Fragment, useEffect, useRef, useState } from "react"

import { octaveLayout } from "../../config/layout";
import { useLayout } from "../../hooks/useLayout"
import { useMidi } from "../../hooks/useMidi";
import { useSequencer } from "../../hooks/useSequencer";

const Keyboard = () => {
const { breakpoint } = useLayout();
const { playNote, octave, input, passthrough } = useMidi();
const { isRecording, stepStart, stepEnd } = useSequencer();
const [ octaves, setOctaves ] = useState( octaveLayout[breakpoint] );
const [ activeNote, setActiveNote ] = useState(null);

const keyboardRef = useRef(null);
const notes = ["C", "D", "E", "F", "G", "A", "B"]

const onPlayStart = (e) => {
window.navigator.vibrate && window.navigator.vibrate(10);
isRecording && stepStart(e.target.dataset.note, 0);
playNote(e.target.dataset.note);
}

const onPlayEnd = (e) => {
isRecording && stepEnd();
playNote(e.target.dataset.note, false);
e.target.blur();
}

const setNote = (e) => {
const note = `${e.note._name}${!!e.note._accidental ? e.note._accidental : ""}${e.note._octave}`
if(e.type === "noteon") setActiveNote(note)
else setActiveNote(false)
}

const events = {
onTouchStart: onPlayStart,
onTouchEnd: onPlayEnd,
onMouseDown: onPlayStart,
onMouseUp: onPlayEnd,
onMouseLeave: onPlayEnd,
onTouchCancel: onPlayEnd
}

useEffect(() => {
setOctaves(octaveLayout[breakpoint])
}, [breakpoint]);

useEffect(() => {
!input?.hasListener("noteon", setNote) && input?.addListener("noteon", setNote)
!input?.hasListener("noteoff", setNote) && input?.addListener("noteoff", setNote)
!passthrough?.hasListener("noteon", setNote) && passthrough?.addListener("noteon", setNote)
!passthrough?.hasListener("noteoff", setNote) && passthrough?.addListener("noteoff", setNote)

return () => {
input?.hasListener("noteon", setNote) && input?.removeListener("noteon", setNote)
input?.hasListener("noteoff", setNote) && input?.removeListener("noteoff", setNote)
passthrough?.hasListener("noteon", setNote) && passthrough?.removeListener("noteon", setNote)
passthrough?.hasListener("noteoff", setNote) && passthrough?.removeListener("noteoff", setNote)
}
}, [input, passthrough])

return (
<div ref={keyboardRef} className="flex-1 grid relative overflow-hidden auto-cols-auto grid-rows-2 gap-x-1 grid-flow-col h-56 -mt-20">
{
[...Array(octaves).keys()].map((o) => {
return [...Array(7).keys()].map((n) => {
return (
<Fragment key={`${o}${n}`}>
{ [1,2,4,5,6].includes(n + 1) && <button {...events } aria-label={ `${notes[n]}#${o + octave}`} data-note={ `${notes[n]}#${o + octave}`} className={`key relative btn btn-secondary row-start-1 top-3/4 w-3/4 left-2/3 h-3/4 rounded z-10 focus:shadow-inner shadow shadow-black ${ activeNote === `${notes[n]}#${o + octave}` && 'btn-pushed'}`}></button> }
<button aria-label={`${notes[n]}${o + octave}`} {...events } data-note={ `${notes[n]}${o + octave}` } className={`key btn btn-active btn-ghost row-start-2 w-full h-full rounded z-0 focus:shadow-inner shadow shadow-black ${ activeNote === `${notes[n]}${o + octave}` && 'btn-pushed'}`}></button>
</Fragment>
)
})
})
}
{ octaves && <button aria-label={ `C${octave + octaves}` } {...events } data-note={ `C${octave + octaves}` } className={`key btn btn-active btn-ghost row-start-2 w-full h-full rounded z-0 focus:shadow-inner shadow shadow-black ${ activeNote === `C${octave + octaves}` && 'btn-pushed'}`}></button> }
</div>
)
}

import { Fragment, useEffect, useRef, useState } from "react"

import { octaveLayout } from "../../config/layout";
import { useLayout } from "../../hooks/useLayout"
import { useMidi } from "../../hooks/useMidi";
import { useSequencer } from "../../hooks/useSequencer";

const Keyboard = () => {
const { breakpoint } = useLayout();
const { playNote, octave, input, passthrough } = useMidi();
const { isRecording, stepStart, stepEnd } = useSequencer();
const [ octaves, setOctaves ] = useState( octaveLayout[breakpoint] );
const [ activeNote, setActiveNote ] = useState(null);

const keyboardRef = useRef(null);
const notes = ["C", "D", "E", "F", "G", "A", "B"]

const onPlayStart = (e) => {
window.navigator.vibrate && window.navigator.vibrate(10);
isRecording && stepStart(e.target.dataset.note, 0);
playNote(e.target.dataset.note);
}

const onPlayEnd = (e) => {
isRecording && stepEnd();
playNote(e.target.dataset.note, false);
e.target.blur();
}

const setNote = (e) => {
const note = `${e.note._name}${!!e.note._accidental ? e.note._accidental : ""}${e.note._octave}`
if(e.type === "noteon") setActiveNote(note)
else setActiveNote(false)
}

const events = {
onTouchStart: onPlayStart,
onTouchEnd: onPlayEnd,
onMouseDown: onPlayStart,
onMouseUp: onPlayEnd,
onMouseLeave: onPlayEnd,
onTouchCancel: onPlayEnd
}

useEffect(() => {
setOctaves(octaveLayout[breakpoint])
}, [breakpoint]);

useEffect(() => {
!input?.hasListener("noteon", setNote) && input?.addListener("noteon", setNote)
!input?.hasListener("noteoff", setNote) && input?.addListener("noteoff", setNote)
!passthrough?.hasListener("noteon", setNote) && passthrough?.addListener("noteon", setNote)
!passthrough?.hasListener("noteoff", setNote) && passthrough?.addListener("noteoff", setNote)

return () => {
input?.hasListener("noteon", setNote) && input?.removeListener("noteon", setNote)
input?.hasListener("noteoff", setNote) && input?.removeListener("noteoff", setNote)
passthrough?.hasListener("noteon", setNote) && passthrough?.removeListener("noteon", setNote)
passthrough?.hasListener("noteoff", setNote) && passthrough?.removeListener("noteoff", setNote)
}
}, [input, passthrough])

return (
<div ref={keyboardRef} className="flex-1 grid relative overflow-hidden auto-cols-auto grid-rows-2 gap-x-1 grid-flow-col h-56 -mt-20">
{
[...Array(octaves).keys()].map((o) => {
return [...Array(7).keys()].map((n) => {
return (
<Fragment key={`${o}${n}`}>
{ [1,2,4,5,6].includes(n + 1) && <button {...events } aria-label={ `${notes[n]}#${o + octave}`} data-note={ `${notes[n]}#${o + octave}`} className={`key relative btn btn-secondary row-start-1 top-3/4 w-3/4 left-2/3 h-3/4 rounded z-10 focus:shadow-inner shadow shadow-black ${ activeNote === `${notes[n]}#${o + octave}` && 'btn-pushed'}`}></button> }
<button aria-label={`${notes[n]}${o + octave}`} {...events } data-note={ `${notes[n]}${o + octave}` } className={`key btn btn-active btn-ghost row-start-2 w-full h-full rounded z-0 focus:shadow-inner shadow shadow-black ${ activeNote === `${notes[n]}${o + octave}` && 'btn-pushed'}`}></button>
</Fragment>
)
})
})
}
{ octaves && <button aria-label={ `C${octave + octaves}` } {...events } data-note={ `C${octave + octaves}` } className={`key btn btn-active btn-ghost row-start-2 w-full h-full rounded z-0 focus:shadow-inner shadow shadow-black ${ activeNote === `C${octave + octaves}` && 'btn-pushed'}`}></button> }
</div>
)
}

export default Keyboard
104 changes: 52 additions & 52 deletions src/components/controls/Knob.js → src/components/controls/Knob.jsx
Original file line number Diff line number Diff line change
@@ -1,53 +1,53 @@
import { useCallback, useEffect, useRef, useState } from "react";

import knob from "../../assets/knob.png";

const Knob = ({id, value = 0, min = 0, max = 127, step = 1, label, onChange}) => {
const [ currentValue, setValue ] = useState(value ? value : min);
const knobRef = useRef(null);

const handleValue = useCallback((e) => {
setValue(e.target.value);
onChange && onChange(parseInt(e.target.value));
}, [onChange])

useEffect(() => setValue(value), [value]);

useEffect(() => {
const currentKnob = knobRef.current;

currentKnob.addEventListener("change", handleValue)
currentKnob.addEventListener("input", handleValue)

return () => {
currentKnob.removeEventListener("change", handleValue)
currentKnob.removeEventListener("input", handleValue)
}
}, [handleValue])

return (
<div className="flex flex-col items-center justify-center">
{ label && <label className="text-secondary text-xs uppercase font-bold" htmlFor={id}>{label}</label> }
<div className="flex-1">
<input
{...(label ? {id: id} : {"aria-label": id}) }
ref={knobRef}
type="range"
name={label}
className="input-knob focus-visible:ring-offset-0"
data-src={ knob }
data-sprites="100"
diameter="90"
value={ currentValue }
min={ min }
max={ max }
step={ step }
onChange={ handleValue }
/>
</div>
<div className="bg-neutral bg-grid font-sevenSegment h-4 w-8 text-xs text-center rounded text-accent outline outline-base-100 outline-offset-1 outline-1">{ value }</div>
</div>
)
}

import { useCallback, useEffect, useRef, useState } from "react";

import knob from "../../assets/knob.png";

const Knob = ({id, value = 0, min = 0, max = 127, step = 1, label, onChange}) => {
const [ currentValue, setValue ] = useState(value ? value : min);
const knobRef = useRef(null);

const handleValue = useCallback((e) => {
setValue(e.target.value);
onChange && onChange(parseInt(e.target.value));
}, [onChange])

useEffect(() => setValue(value), [value]);

useEffect(() => {
const currentKnob = knobRef.current;

currentKnob.addEventListener("change", handleValue)
currentKnob.addEventListener("input", handleValue)

return () => {
currentKnob.removeEventListener("change", handleValue)
currentKnob.removeEventListener("input", handleValue)
}
}, [handleValue])

return (
<div className="flex flex-col items-center justify-center">
{ label && <label className="text-secondary text-xs uppercase font-bold" htmlFor={id}>{label}</label> }
<div className="flex-1">
<input
{...(label ? {id: id} : {"aria-label": id}) }
ref={knobRef}
type="range"
name={label}
className="input-knob focus-visible:ring-offset-0"
data-src={ knob }
data-sprites="100"
diameter="90"
value={ currentValue }
min={ min }
max={ max }
step={ step }
onChange={ handleValue }
/>
</div>
<div className="bg-neutral bg-grid font-sevenSegment h-4 w-8 text-xs text-center rounded text-accent outline outline-base-100 outline-offset-1 outline-1">{ value }</div>
</div>
)
}

export default Knob;
Loading

0 comments on commit 17ff36b

Please sign in to comment.