mirror of
https://github.com/patarapolw/cjdic2.git
synced 2026-11-09 17:39:35 +00:00
plan loading message dialog
This commit is contained in:
1 parent
69f931f2a1
commit
9b5ae1cd8b
4 files changed
+230
-182
No files matched your search
+4
-182
@@ -1,192 +1,14 @@
|
||||
import "./App.css";
|
||||
|
||||
import { CompositionEventHandler, useEffect, useState } from "react";
|
||||
|
||||
import { Box, Button, Card, Group, Input, Stack } from "@chakra-ui/react";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
|
||||
import Glossary from "./components/Glossary";
|
||||
import LoadingDialog from "./components/LoadingDialog";
|
||||
import { Provider } from "./components/ui/provider";
|
||||
|
||||
interface Entry {
|
||||
term: string;
|
||||
reading: string;
|
||||
glossary_json: string;
|
||||
def_tags: string;
|
||||
rules: string;
|
||||
score: number;
|
||||
sequence: number;
|
||||
term_tags: string;
|
||||
dict_title: string;
|
||||
}
|
||||
import SearchPage from "./pages/SearchPage";
|
||||
|
||||
function App() {
|
||||
const [q, setQ] = useState("");
|
||||
const [entries, setEntries] = useState<Entry[]>([]);
|
||||
const [searchTimeout, setSearchTimeout] = useState(0);
|
||||
const [furigana, setFurigana] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
trySearch();
|
||||
}, [q]);
|
||||
|
||||
async function trySearch() {
|
||||
if (!q.trim()) {
|
||||
setEntries([]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (searchTimeout) {
|
||||
clearTimeout(searchTimeout);
|
||||
}
|
||||
|
||||
setSearchTimeout(
|
||||
setTimeout(() => {
|
||||
runSearch();
|
||||
}, 250),
|
||||
);
|
||||
}
|
||||
|
||||
async function runSearch() {
|
||||
const ender = /\p{Z}$/u.test(q) ? "" : "*";
|
||||
|
||||
const re = /\<(.+?)\>\[(.+?)\]/g;
|
||||
|
||||
let qTerm = "";
|
||||
let qReading = "";
|
||||
|
||||
q.trim()
|
||||
.split(re)
|
||||
.map((s, i) => {
|
||||
switch (i % 3) {
|
||||
case 1:
|
||||
qTerm += s;
|
||||
break;
|
||||
case 2:
|
||||
qReading += s;
|
||||
break;
|
||||
default:
|
||||
qTerm += s;
|
||||
qReading += s;
|
||||
}
|
||||
});
|
||||
|
||||
qTerm += ender;
|
||||
qReading += ender;
|
||||
|
||||
setEntries(
|
||||
await invoke("search_yomitan", {
|
||||
qTerm,
|
||||
qReading,
|
||||
limit: 10,
|
||||
offset: 0,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
const FURIGANA_REGEX = /^[\p{scx=Hiragana}\p{scx=Katakana}]+$/u;
|
||||
const KANJI_REGEX = /([\p{sc=Han}\p{N}々〆ヵヶ]+)/u;
|
||||
|
||||
const updateFurigana: CompositionEventHandler = ({
|
||||
data: compositionData,
|
||||
}) => {
|
||||
if (FURIGANA_REGEX.test(compositionData)) {
|
||||
setFurigana(compositionData);
|
||||
}
|
||||
};
|
||||
|
||||
const addFurigana: CompositionEventHandler = ({ data: compositionData }) => {
|
||||
const cleanedFuri = [...furigana.replace(/n/g, "ん")]
|
||||
.map((c) => katakanaToHiragana(c))
|
||||
.join("");
|
||||
|
||||
let parts = compositionData.split(KANJI_REGEX);
|
||||
if (parts.length === 1) return;
|
||||
|
||||
const hiraganaParts = parts.map((p) =>
|
||||
[...p].map((c) => katakanaToHiragana(c)).join(""),
|
||||
);
|
||||
const regex = new RegExp(
|
||||
`^${hiraganaParts.map((p, idx) => `(${idx & 1 ? ".+" : p})`).join("")}$`,
|
||||
);
|
||||
let rt: (string | null)[] = furigana.match(regex) || [];
|
||||
if (!rt.length) {
|
||||
parts = [compositionData];
|
||||
rt = [null, cleanedFuri];
|
||||
}
|
||||
rt.shift();
|
||||
|
||||
const markup = parts
|
||||
.map((p, idx) => (idx & 1 ? `<${p}>[${rt[idx]}]` : p))
|
||||
.join("");
|
||||
setQ(markup);
|
||||
};
|
||||
|
||||
function katakanaToHiragana(k: string) {
|
||||
let c = k.charCodeAt(0);
|
||||
return c >= 12449 && c <= 12531
|
||||
? String.fromCharCode(k.charCodeAt(0) - 96)
|
||||
: k;
|
||||
}
|
||||
|
||||
return (
|
||||
<Provider>
|
||||
<Stack maxW={"1000px"} margin={"0.5em auto"}>
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
}}
|
||||
>
|
||||
<Group attached w="full">
|
||||
<Input
|
||||
id="greet-input"
|
||||
value={q}
|
||||
onChange={(e) => setQ(e.currentTarget.value)}
|
||||
onCompositionUpdate={updateFurigana}
|
||||
onCompositionEnd={addFurigana}
|
||||
autoComplete="off"
|
||||
spellCheck={false}
|
||||
placeholder="Search..."
|
||||
/>
|
||||
<Button type="submit">Search</Button>
|
||||
</Group>
|
||||
</form>
|
||||
<Box as={"ol"} listStyleType={"number"} style={{ margin: "1em" }}>
|
||||
{entries.map(
|
||||
({ term, reading, dict_title, glossary_json, ...it }, i) => (
|
||||
<li key={i}>
|
||||
<details>
|
||||
<summary>
|
||||
{term} ({reading}) 【{dict_title}】
|
||||
</summary>
|
||||
|
||||
<Card.Root>
|
||||
<Card.Header>
|
||||
{JSON.stringify(it, (_, v) => v || undefined)}
|
||||
</Card.Header>
|
||||
<Card.Body>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
whiteSpace: "pre-wrap",
|
||||
}}
|
||||
>
|
||||
{JSON.parse(glossary_json).map((g: any) => (
|
||||
<Glossary
|
||||
glossary={g}
|
||||
onTermClicked={(t) => setQ(t + " ")}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</Card.Body>
|
||||
</Card.Root>
|
||||
</details>
|
||||
</li>
|
||||
),
|
||||
)}
|
||||
</Box>
|
||||
</Stack>
|
||||
<SearchPage />
|
||||
<LoadingDialog />
|
||||
</Provider>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user