Closed Ryo4499 closed 4 months ago
// pages/index.tsx import React, { useState, useEffect, useCallback } from 'react'; import Head from 'next/head'; import { ThemeProvider, createTheme, CssBaseline, Container, Typography, Button, Box, Paper } from '@mui/material'; import RefreshIcon from '@mui/icons-material/Refresh'; const theme = createTheme({ palette: { mode: 'light', primary: { main: '#4CAF50', }, secondary: { main: '#f44336', }, }, }); const morseCode: { [key: string]: string } = { '.-': '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', '.----': '1', '..---': '2', '...--': '3', '....-': '4', '.....': '5', '-....': '6', '--...': '7', '---..': '8', '----.': '9', '-----': '0' }; const Home: React.FC = () => { const [morseInput, setMorseInput] = useState<string>(''); const [currentLetter, setCurrentLetter] = useState<string>(''); const [result, setResult] = useState<string>(''); const [isPressed, setIsPressed] = useState<boolean>(false); const dotDuration = 250; // milliseconds const startPress = useCallback(() => { setIsPressed(true); }, []); const endPress = useCallback(() => { setIsPressed(false); const duration = new Date().getTime() - pressStartTime; setCurrentLetter(prev => prev + (duration < dotDuration ? '.' : '-')); }, []); const addLetter = useCallback(() => { if (currentLetter) { setMorseInput(prev => prev + currentLetter + ' '); setCurrentLetter(''); } }, [currentLetter]); const updateResult = useCallback(() => { const decoded = morseInput .trim() .split(' ') .map(code => morseCode[code] || '?') .join(''); setResult(decoded); }, [morseInput]); const resetInput = useCallback(() => { setMorseInput(''); setCurrentLetter(''); setResult(''); }, []); useEffect(() => { updateResult(); }, [morseInput, updateResult]); useEffect(() => { const timer = setTimeout(addLetter, dotDuration * 3); return () => clearTimeout(timer); }, [currentLetter, addLetter]); useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { if (e.code === 'Space' && !e.repeat) { e.preventDefault(); startPress(); } }; const handleKeyUp = (e: KeyboardEvent) => { if (e.code === 'Space') { e.preventDefault(); endPress(); } }; window.addEventListener('keydown', handleKeyDown); window.addEventListener('keyup', handleKeyUp); return () => { window.removeEventListener('keydown', handleKeyDown); window.removeEventListener('keyup', handleKeyUp); }; }, [startPress, endPress]); let pressStartTime: number; return ( <ThemeProvider theme={theme}> <CssBaseline /> <Head> <title>モールス信号練習</title> <meta name="description" content="モールス信号練習アプリ" /> <link rel="icon" href="/favicon.ico" /> </Head> <Container maxWidth="sm"> <Box sx={{ my: 4, textAlign: 'center' }}> <Typography variant="h3" component="h1" gutterBottom> モールス信号練習 </Typography> <Typography variant="body1" gutterBottom> クリックまたはスペースキーで入力<br /> 短押し: ・ 長押し: - </Typography> <Button variant="contained" color="primary" size="large" sx={{ width: 200, height: 200, borderRadius: '50%', fontSize: '1.2rem', my: 2, textTransform: 'none', backgroundColor: isPressed ? theme.palette.primary.dark : theme.palette.primary.main, }} onMouseDown={() => { pressStartTime = new Date().getTime(); startPress(); }} onMouseUp={endPress} onMouseLeave={endPress} > ここをクリック<br />またはスペースキー </Button> <Paper elevation={3} sx={{ p: 2, my: 2 }}> <Typography variant="h6" gutterBottom> 入力: </Typography> <Typography variant="body1"> {morseInput + currentLetter} </Typography> </Paper> <Paper elevation={3} sx={{ p: 2, my: 2 }}> <Typography variant="h6" gutterBottom> 変換結果: </Typography> <Typography variant="body1"> {result} </Typography> </Paper> <Button variant="contained" color="secondary" startIcon={<RefreshIcon />} onClick={resetInput} > リセット </Button> </Box> </Container> </ThemeProvider> ); }; export default Home;