mirror of
https://github.com/genemecija/learn-morse-code.git
synced 2026-01-20 07:10:20 +01:00
Initial commit
This commit is contained in:
parent
482002d111
commit
b11506f6de
142
src/App.css
142
src/App.css
|
|
@ -1,38 +1,122 @@
|
|||
.App {
|
||||
text-align: center;
|
||||
html, body {
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
#root {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
#root div {
|
||||
width: 95vw;
|
||||
}
|
||||
|
||||
.App-logo {
|
||||
height: 40vmin;
|
||||
pointer-events: none;
|
||||
#morseButton {
|
||||
width: 200px !important;
|
||||
height: 200px;
|
||||
background: goldenrod;
|
||||
border-radius: 100px;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
.App-logo {
|
||||
animation: App-logo-spin infinite 20s linear;
|
||||
}
|
||||
#morseBufferDisplay {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
/* border: 1px solid green; */
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
}
|
||||
/* #morseBufferDisplay div {
|
||||
width: 50% !important;
|
||||
border: 1px dotted orange;
|
||||
} */
|
||||
#morseBufferDisplay #ditDahs {
|
||||
width: 50%;
|
||||
padding-right: 5px;
|
||||
border-right: 2px solid #000;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.ditDah {
|
||||
background: #DDD;
|
||||
height: 40px;
|
||||
width: 30px !important;
|
||||
margin-left: 3px;
|
||||
box-shadow: 0px 1px 0px #000;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 35px;
|
||||
font-family: 'Courier';
|
||||
font-weight: bold;
|
||||
}
|
||||
.space {
|
||||
font-size: 20px;
|
||||
}
|
||||
.morseError {
|
||||
background: rgba(255,0,0,0.4);
|
||||
}
|
||||
|
||||
.App-header {
|
||||
background-color: #282c34;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: calc(10px + 2vmin);
|
||||
color: white;
|
||||
#morseBufferDisplay #alphanumeric {
|
||||
padding-left: 5px;
|
||||
border-left: 2px solid #000;
|
||||
width: 50%;
|
||||
font-size: 40px;
|
||||
font-family: 'Courier';
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.App-link {
|
||||
color: #61dafb;
|
||||
#morseDisplay {
|
||||
/* border: 2px solid red; */
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
@keyframes App-logo-spin {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
#cardContainer div .morseCard:nth-child(1){
|
||||
opacity: 100%;
|
||||
}
|
||||
#cardContainer div .morseCard:nth-child(2){
|
||||
opacity: 70%;
|
||||
}
|
||||
#cardContainer div .morseCard:nth-child(3){
|
||||
opacity: 50%;
|
||||
}
|
||||
#cardContainer div .morseCard:nth-child(4){
|
||||
opacity: 30%;
|
||||
}
|
||||
#cardContainer div .morseCard:nth-child(5){
|
||||
opacity: 10%;
|
||||
}
|
||||
.morseCard {
|
||||
display: flex;
|
||||
flex-direction: row !important;
|
||||
justify-content: center;
|
||||
/* border: 1px solid blue; */
|
||||
margin-bottom: 2px;
|
||||
font-size: 40px;
|
||||
font-family: 'Courier';
|
||||
font-weight: bold;
|
||||
}
|
||||
.morseCard div {
|
||||
/* border: 1px solid green; */
|
||||
padding: 5px;
|
||||
margin: 2px;
|
||||
/* border: 1px solid #EEE; */
|
||||
background: #EEE;
|
||||
/* box-shadow: 0px 2px 2px -2px #666; */
|
||||
}
|
||||
.morseCard .ditDahs {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
/* #morseBufferDisplay {
|
||||
border: 1px solid green;
|
||||
}
|
||||
#morseDisplay #cardContainer div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
} */
|
||||
|
|
|
|||
163
src/App.js
163
src/App.js
|
|
@ -1,26 +1,149 @@
|
|||
import React from 'react';
|
||||
import logo from './logo.svg';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import './App.css';
|
||||
import MorseDisplay from './components/MorseDisplay'
|
||||
import MorseBufferDisplay from './components/MorseBufferDisplay'
|
||||
import MorseButton from './components/MorseButton'
|
||||
// import LettersDisplay from './components/LettersDisplay'
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<div className="App">
|
||||
<header className="App-header">
|
||||
<img src={logo} className="App-logo" alt="logo" />
|
||||
<p>
|
||||
Edit <code>src/App.js</code> and save to reload.
|
||||
</p>
|
||||
<a
|
||||
className="App-link"
|
||||
href="https://reactjs.org"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Learn React
|
||||
</a>
|
||||
</header>
|
||||
</div>
|
||||
);
|
||||
|
||||
const [morseCharBuffer, setMorseCharBuffer] = useState('') // e.g. '-..'
|
||||
// const [morseLettersBuffer, setMorseLettersBuffer] = useState([]) // e.g. ['-..','.','-,']
|
||||
const [morseWords, setMorseWords] = useState([]) // e.g. [['-..','.','-,'], ['...','---','...']]
|
||||
|
||||
let charTimer = 0
|
||||
let charTime = 0
|
||||
let gapTimer = 0
|
||||
let gapTime = 0
|
||||
|
||||
const timingUnit = 15 // default: 25
|
||||
|
||||
const ditMaxTime = 5 // default: 3
|
||||
const letterGapMinTime = ditMaxTime*3
|
||||
const wordGapMaxTime = ditMaxTime*7
|
||||
const morseHistorySize = 3
|
||||
|
||||
let context = new AudioContext()
|
||||
let o
|
||||
|
||||
|
||||
function handleInputStart(event) {
|
||||
if ((event.keyCode !== 32 && event.target.id !== "morseButton") ||
|
||||
(event.repeat)) {
|
||||
return
|
||||
}
|
||||
// if (gapTimer===0) { setMorseLettersBuffer('') }
|
||||
checkGapBetweenInputs()
|
||||
clearInterval(gapTimer)
|
||||
|
||||
o = context.createOscillator()
|
||||
o.type = "sine"
|
||||
let frequency = 550.0
|
||||
o.frequency.value = frequency
|
||||
o.connect(context.destination)
|
||||
o.start()
|
||||
|
||||
startCharTimer()
|
||||
}
|
||||
function startCharTimer() {
|
||||
// Reset character time
|
||||
charTime = 0
|
||||
// Start Character Timer
|
||||
charTimer = setInterval(() => {
|
||||
charTime += 1
|
||||
}, timingUnit);
|
||||
}
|
||||
|
||||
function handleInputEnd(event) {
|
||||
if ((event.keyCode !== 32 && event.target.id !== "morseButton") ||
|
||||
(event.repeat)) {
|
||||
return
|
||||
}
|
||||
|
||||
if (charTime <= ditMaxTime) {
|
||||
setMorseCharBuffer(prev => prev + '.')
|
||||
} else {
|
||||
setMorseCharBuffer(prev => prev + '-')
|
||||
}
|
||||
|
||||
stopCharTimer()
|
||||
startGapTimer()
|
||||
o.stop()
|
||||
}
|
||||
|
||||
function stopCharTimer() {
|
||||
clearInterval(charTimer)
|
||||
charTimer = 0
|
||||
}
|
||||
|
||||
function startGapTimer() {
|
||||
gapTime = 0
|
||||
gapTimer = setInterval(() => {
|
||||
gapTime += 1
|
||||
|
||||
// Gap between words
|
||||
if (gapTime >= wordGapMaxTime) {
|
||||
setMorseCharBuffer(prev => prev + '/')
|
||||
clearInterval(gapTimer)
|
||||
gapTimer = 0
|
||||
gapTime = 0
|
||||
}
|
||||
}, timingUnit);
|
||||
}
|
||||
|
||||
function checkGapBetweenInputs() {
|
||||
// Check Gap between letters
|
||||
if ((gapTime >= letterGapMinTime) && (gapTime < wordGapMaxTime)) {
|
||||
// setMorseLettersBuffer(prev => [...prev, morseCharBuffer])
|
||||
setMorseCharBuffer(prev => prev + ' ')
|
||||
// morseLettersBuffer = [...morseLettersBuffer, morseCharBuffer]
|
||||
// setMorseCharBuffer('')
|
||||
|
||||
clearInterval(gapTimer)
|
||||
gapTimer = 0
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
document.addEventListener('keydown', handleInputStart)
|
||||
document.addEventListener('keyup', handleInputEnd)
|
||||
document.getElementById('morseButton').addEventListener('mousedown', handleInputStart)
|
||||
document.getElementById('morseButton').addEventListener('mouseup', handleInputEnd)
|
||||
// eslint-disable-next-line
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (morseCharBuffer.slice(-1) === ' ') {
|
||||
// setMorseLettersBuffer([morseCharBuffer])
|
||||
// setMorseCharBuffer('')
|
||||
}
|
||||
if (morseCharBuffer.slice(-1) === '/') {
|
||||
// Remove forward slash
|
||||
let val = morseCharBuffer.slice(0,morseCharBuffer.length-1)
|
||||
console.log('val: ', val);
|
||||
|
||||
setMorseWords(prev => [val, ...prev])
|
||||
|
||||
if (morseWords.length >= morseHistorySize) {
|
||||
setMorseWords(prev => prev.slice(0,prev.length-1))
|
||||
}
|
||||
|
||||
setMorseCharBuffer('')
|
||||
}
|
||||
|
||||
// setMorseLettersBuffer(prev => [...prev, morseCharBuffer])
|
||||
// eslint-disable-next-line
|
||||
}, [morseCharBuffer])
|
||||
|
||||
return (
|
||||
<div>charTime: {charTime}<br/>
|
||||
<MorseButton />
|
||||
morseCharBuffer:<br/>
|
||||
<MorseBufferDisplay buffer={morseCharBuffer} /><br/>
|
||||
<MorseDisplay morseWords={morseWords}/>
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
|
|
|
|||
4
src/MorseCard.css
Normal file
4
src/MorseCard.css
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
.morseCard {
|
||||
border: 1px solid blue;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
23
src/components/AlphaNumeric.js
Normal file
23
src/components/AlphaNumeric.js
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
import React from "react"
|
||||
import morseCode from '../data/morse-reverse.json'
|
||||
|
||||
function AlphaNumeric(props) {
|
||||
|
||||
if (props.morseLetter === '') {
|
||||
return (
|
||||
<span></span>
|
||||
)
|
||||
} else {
|
||||
if (morseCode[props.morseLetter] === undefined) {
|
||||
return (
|
||||
<span className='morseError'>[?]</span>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<span>{morseCode[props.morseLetter].toUpperCase()}</span>
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default AlphaNumeric
|
||||
23
src/components/DitDah.js
Normal file
23
src/components/DitDah.js
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
import React from "react"
|
||||
import morseCode from '../data/morse-reverse.json'
|
||||
|
||||
function DitDah(props) {
|
||||
|
||||
if (props.morseLetter === '') {
|
||||
return (
|
||||
<span className='space'> </span>
|
||||
)
|
||||
} else {
|
||||
if (morseCode[props.morseLetter] === undefined) {
|
||||
return (
|
||||
<span className='morseError'>{props.morseLetter}</span>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<span>{props.morseLetter}</span>
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default DitDah
|
||||
12
src/components/DitDahDisplay.js
Normal file
12
src/components/DitDahDisplay.js
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import React from "react"
|
||||
|
||||
function DitDahDisplay(props) {
|
||||
|
||||
return (
|
||||
<div className='ditDah'>
|
||||
{props.dd}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default DitDahDisplay
|
||||
44
src/components/MorseBufferDisplay.js
Normal file
44
src/components/MorseBufferDisplay.js
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
import React from "react"
|
||||
import DitDahDisplay from "./DitDahDisplay"
|
||||
import morseCode from '../data/morse-reverse.json'
|
||||
|
||||
function MorseBufferDisplay(props) {
|
||||
|
||||
let ditDahs = props.buffer.split('').map((ditdah,index) => <DitDahDisplay key={index} dd={ditdah} />)
|
||||
|
||||
let alphanumeric = ''
|
||||
if (props.buffer.includes(' ')) {
|
||||
let letters = props.buffer.split(' ')
|
||||
for (let i in letters) {
|
||||
if (letters[i] === ' ') {
|
||||
alphanumeric += ' '
|
||||
} else {
|
||||
if (morseCode[letters[i]] === undefined) {
|
||||
// alphanumeric += '[?]'
|
||||
} else {
|
||||
alphanumeric += morseCode[letters[i]]
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (props.buffer !== '') {
|
||||
let letters = props.buffer
|
||||
if (morseCode[letters] === undefined) {
|
||||
alphanumeric += '[?]'
|
||||
} else {
|
||||
alphanumeric += morseCode[letters]
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div id="morseBufferDisplay">
|
||||
<div id="ditDahs">
|
||||
{ditDahs}
|
||||
</div>
|
||||
<div id="alphanumeric">
|
||||
{alphanumeric.toUpperCase()}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default MorseBufferDisplay
|
||||
10
src/components/MorseButton.js
Normal file
10
src/components/MorseButton.js
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import React from "react"
|
||||
|
||||
function MorseButton() {
|
||||
|
||||
return (
|
||||
<div id="morseButton"></div>
|
||||
)
|
||||
}
|
||||
|
||||
export default MorseButton
|
||||
35
src/components/MorseCard.js
Normal file
35
src/components/MorseCard.js
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
import React from "react"
|
||||
import AlphaNumeric from './AlphaNumeric'
|
||||
import DitDah from './DitDah'
|
||||
|
||||
function MorseCard(props) {
|
||||
|
||||
let morseComponent
|
||||
let alphaNumericsComponent
|
||||
|
||||
if (props.morse.includes(' ')) {
|
||||
let letters = props.morse.split(' ').join(' ').split(' ')
|
||||
morseComponent = letters.map((letter, index) => <DitDah key={index} morseLetter={letter}/>)
|
||||
|
||||
alphaNumericsComponent = letters.map((letter, index) => <AlphaNumeric key={index} morseLetter={letter}/>)
|
||||
}
|
||||
else {
|
||||
let letter = props.morse
|
||||
|
||||
morseComponent = <DitDah morseLetter={letter} />
|
||||
alphaNumericsComponent = <AlphaNumeric morseLetter={letter} />
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='morseCard'>
|
||||
<div className='ditDahs'>
|
||||
{morseComponent}
|
||||
</div>
|
||||
<div className='alphanumeric'>
|
||||
{alphaNumericsComponent}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default MorseCard
|
||||
17
src/components/MorseDisplay.js
Normal file
17
src/components/MorseDisplay.js
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import React from "react"
|
||||
import MorseCard from './MorseCard'
|
||||
|
||||
function MorseDisplay(props) {
|
||||
|
||||
let morseCards = props.morseWords.map((word,index) => <MorseCard key={index} morse={word} />)
|
||||
|
||||
return (
|
||||
<div id="morseDisplay">
|
||||
<div id="cardContainer">
|
||||
<div>{morseCards}</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default MorseDisplay
|
||||
47
src/data/morse-reverse.json
Normal file
47
src/data/morse-reverse.json
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
{
|
||||
"-----": "0",
|
||||
".----": "1",
|
||||
"..---": "2",
|
||||
"...--": "3",
|
||||
"....-": "4",
|
||||
".....": "5",
|
||||
"-....": "6",
|
||||
"--...": "7",
|
||||
"---..": "8",
|
||||
"----.": "9",
|
||||
".-": "a",
|
||||
"-...": "b",
|
||||
"-.-.": "c",
|
||||
"-..": "d",
|
||||
".": "e",
|
||||
"..-.": "f",
|
||||
"--.": "g",
|
||||
"....": "h",
|
||||
"..": "i",
|
||||
".---": "j",
|
||||
"-.-": "k",
|
||||
".-..": "l",
|
||||
"--": "m",
|
||||
"-.": "n",
|
||||
"---": "o",
|
||||
".--.": "p",
|
||||
"--.-": "q",
|
||||
".-.": "r",
|
||||
"...": "s",
|
||||
"-": "t",
|
||||
"..-": "u",
|
||||
"...-": "v",
|
||||
".--": "w",
|
||||
"-..-": "x",
|
||||
"-.--": "y",
|
||||
"--..": "z",
|
||||
".-.-.-": ".",
|
||||
"--..--": ",",
|
||||
"..--..": "?",
|
||||
"-.-.--": "!",
|
||||
"-....-": "-",
|
||||
"-..-.": "/",
|
||||
".--.-.": "@",
|
||||
"-.--.": "(",
|
||||
"-.--.-": ")"
|
||||
}
|
||||
Loading…
Reference in a new issue