Files
learn-languages/src/app/word-board/page.tsx

188 lines
5.5 KiB
TypeScript

"use client";
import TheBoard from "@/app/word-board/TheBoard";
import LightButton from "../../components/buttons/LightButton";
import { KeyboardEvent, useRef, useState } from "react";
import { Word } from "@/interfaces";
import {
BOARD_WIDTH,
TEXT_WIDTH,
BOARD_HEIGHT,
TEXT_SIZE,
} from "@/config/word-board-config";
import { inspect } from "@/utils";
import { Navbar } from "@/components/Navbar";
export default function WordBoard() {
const inputRef = useRef<HTMLInputElement>(null);
const inputFileRef = useRef<HTMLInputElement>(null);
const initialWords = [
// 'apple',
// 'banana',
// 'cannon',
// 'desktop',
// 'kernel',
// 'system',
// 'programming',
// 'owe'
] as Array<string>;
const [words, setWords] = useState(
initialWords.map((v: string) => ({
word: v,
x: Math.random(),
y: Math.random(),
})),
);
const generateNewWord = (word: string) => {
const isOK = (w: Word) => {
if (words.length === 0) return true;
const tf = (ww: Word) =>
({
word: ww.word,
x: Math.floor(ww.x * (BOARD_WIDTH - TEXT_WIDTH * ww.word.length)),
y: Math.floor(ww.y * (BOARD_HEIGHT - TEXT_SIZE)),
}) as Word;
const tfd_words = words.map(tf);
const tfd_w = tf(w);
for (const www of tfd_words) {
const p1 = {
x: (www.x + www.x + TEXT_WIDTH * www.word.length) / 2,
y: (www.y + www.y + TEXT_SIZE) / 2,
};
const p2 = {
x: (tfd_w.x + tfd_w.x + TEXT_WIDTH * tfd_w.word.length) / 2,
y: (tfd_w.y + tfd_w.y + TEXT_SIZE) / 2,
};
if (
Math.abs(p1.x - p2.x) <
(TEXT_WIDTH * (www.word.length + tfd_w.word.length)) / 2 &&
Math.abs(p1.y - p2.y) < TEXT_SIZE
) {
return false;
}
}
return true;
};
let new_word;
let count = 0;
do {
new_word = {
word: word,
x: Math.random(),
y: Math.random(),
};
if (++count > 1000) return null;
} while (!isOK(new_word));
return new_word as Word;
};
const insertWord = () => {
if (!inputRef.current) return;
const word = inputRef.current.value.trim();
if (word === "") return;
const new_word = generateNewWord(word);
if (!new_word) return;
setWords([...words, new_word]);
inputRef.current.value = "";
};
const deleteWord = () => {
if (!inputRef.current) return;
const word = inputRef.current.value.trim();
if (word === "") return;
setWords(words.filter((v) => v.word !== word));
inputRef.current.value = "";
};
const importWords = () => {
inputFileRef.current?.click();
};
const exportWords = () => {
const blob = new Blob([JSON.stringify(words)], {
type: "application/json",
});
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = `${Date.now()}.json`;
a.style.display = "none";
a.click();
URL.revokeObjectURL(url);
};
const handleFileChange = () => {
const files = inputFileRef.current?.files;
if (files && files.length > 0) {
const reader = new FileReader();
reader.onload = () => {
if (reader.result && typeof reader.result === "string")
setWords(JSON.parse(reader.result) as [Word]);
};
reader.readAsText(files[0]);
}
};
const deleteAll = () => {
setWords([] as Array<Word>);
};
const handleKeyDown = (e: KeyboardEvent<HTMLDivElement>) => {
// e.preventDefault();
if (e.key === "Enter") {
insertWord();
}
};
const selectWord = (word: string) => {
if (!inputRef.current) return;
inputRef.current.value = word;
};
const searchWord = () => {
if (!inputRef.current) return;
const word = inputRef.current.value.trim();
if (word === "") return;
inspect(word)();
inputRef.current.value = "";
};
// const readWordAloud = () => {
// playFromUrl('https://fanyi.baidu.com/gettts?lan=uk&text=disclose&spd=3')
// return;
// if (!inputRef.current) return;
// const word = inputRef.current.value.trim();
// if (word === '') return;
// inspect(word)();
// inputRef.current.value = '';
// }
return (
<>
<Navbar></Navbar>
<div className="flex w-screen h-screen justify-center items-center">
<div
onKeyDown={handleKeyDown}
className="p-5 bg-gray-200 rounded shadow-2xl"
>
<TheBoard
selectWord={selectWord}
words={words as [Word]}
setWords={setWords}
/>
<div className="flex justify-center rounded mt-3 gap-1">
<input
ref={inputRef}
placeholder="word to operate"
type="text"
className="focus:outline-none border-b-2 border-black"
/>
<LightButton onClick={insertWord}></LightButton>
<LightButton onClick={deleteWord}></LightButton>
<LightButton onClick={searchWord}></LightButton>
<LightButton onClick={importWords}></LightButton>
<LightButton onClick={exportWords}></LightButton>
<LightButton onClick={deleteAll}></LightButton>
{/* <Button label="朗读" onClick={readWordAloud}></Button> */}
</div>
<input
type="file"
ref={inputFileRef}
className="hidden"
accept="application/json"
onChange={handleFileChange}
></input>
</div>
</div>
</>
);
}