summaryrefslogtreecommitdiff
path: root/packages/prosody-ui/src/components/word/FullWordData.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'packages/prosody-ui/src/components/word/FullWordData.tsx')
-rw-r--r--packages/prosody-ui/src/components/word/FullWordData.tsx156
1 files changed, 156 insertions, 0 deletions
diff --git a/packages/prosody-ui/src/components/word/FullWordData.tsx b/packages/prosody-ui/src/components/word/FullWordData.tsx
new file mode 100644
index 0000000..9b1fc69
--- /dev/null
+++ b/packages/prosody-ui/src/components/word/FullWordData.tsx
@@ -0,0 +1,156 @@
+import React, { useCallback, useEffect, useState } from "react";
+import spinner from "../assets/icons/spinner.svg";
+import likeIcon from "../assets/icons/heart.svg";
+import commentsIcon from "../assets/icons/quote.svg";
+import shareIcon from "../assets/icons/share.svg";
+import fontIcon from "../assets/icons/font.svg";
+import bookmarkIcon from "@/assets/icons/bookmark.svg";
+import speakerIcon from "@/assets/icons/speaker.svg";
+import type { AnalyzeRes, ColorTheme, Meaning } from "@/logic/types";
+import { ColoredText } from "../Sentence.tsx";
+import { P, Span, useSpeechSynthesis } from "@/hooks/useLang.tsx";
+import type { FullWordData } from "@sortug/langlib";
+import { cycleNext } from "@sortug/lib";
+import FontChanger from "@/fonts/FontChanger.tsx";
+import Phonetic from "./Phonetic.tsx";
+
+function Word({
+ data,
+ lang,
+ theme,
+}: {
+ data: FullWordData;
+ lang: string;
+ theme: ColorTheme;
+}) {
+ async function load() {
+ // const wiki = await fetchWiki(data.word);
+ // console.log(wiki, "wiki res");
+ // if ("ok" in wiki) setM(wiki.ok.meanings);
+ // else setError(wiki.error);
+ // setLoading(false);
+ }
+ useEffect(() => {
+ load();
+ }, []);
+ const [error, setError] = useState("");
+ const [loading, setLoading] = useState(false);
+ const [meanings, setM] = useState<Meaning[]>([]);
+ const [fontIdx, setFont] = useState(0);
+
+ const { voices, speaking, speak, stop } = useSpeechSynthesis();
+ function playAudio() {
+ console.log({ voices, speaking });
+ console.log("word", data);
+ speak(data.spelling);
+ }
+ console.log({ data });
+
+ async function saveW() {}
+
+ return (
+ <div id="word-modal" title={data.spelling}>
+ <FontChanger text={data.spelling}>
+ <img className="save-icon cp" onClick={saveW} src={bookmarkIcon} />
+ <div className="original">
+ <ColoredText
+ frags={data.phonetic.syllables.map((s) => ({
+ data: s,
+ display: s.spelling,
+ colorBy: s.tone.name,
+ }))}
+ theme={theme}
+ />
+ </div>
+ <Phonetic data={data} lang={lang} theme={theme} />
+ <div className="pronunciation IPA flex1 flex-center">
+ <P>{data.phonetic.ipa}</P>
+ <img onClick={playAudio} className="icon cp" src={speakerIcon} />
+ </div>
+ <div className="meanings">
+ {loading ? (
+ <img src={spinner} className="spinner bc" />
+ ) : (
+ data.senses.map((m) => (
+ <div key={JSON.stringify(m)} className="meaning">
+ <div className="pos">
+ <Span>{m.pos}</Span>
+ </div>
+ <ol>
+ {m.glosses.map((t, i) => (
+ <li key={t + i} className="translation">
+ <P>{t}</P>
+ </li>
+ ))}
+ </ol>
+ </div>
+ ))
+ )}
+ {error && <div className="error">{error}</div>}
+ </div>
+ </FontChanger>
+ </div>
+ );
+}
+
+export default Word;
+
+<Card className="absolute inset-0 backface-hidden rotate-y-180 flex flex-col overflow-hidden border-slate-200 dark:border-slate-800 shadow-lg bg-slate-50/50">
+ <div className="flex-1 overflow-hidden flex flex-col">
+ <Tabs defaultValue="meanings" className="flex-1 flex flex-col">
+ <div className="px-6 pt-6 pb-2 bg-white border-b">
+ <TabsList className="grid w-full grid-cols-3">
+ <TabsTrigger value="meanings">Meanings</TabsTrigger>
+ <TabsTrigger value="grammar">Grammar</TabsTrigger>
+ <TabsTrigger value="examples">Examples</TabsTrigger>
+ </TabsList>
+ </div>
+
+ <div className="flex-1 overflow-y-auto p-6">
+ <TabsContent value="meanings" className="mt-0 space-y-4">
+ <EnhancedWordMeanings word={word} />
+ </TabsContent>
+
+ <TabsContent value="grammar" className="mt-0 space-y-6">
+ <div className="space-y-4">
+ <div className="bg-blue-50 p-4 rounded-lg border border-blue-100">
+ <h3 className="font-semibold text-blue-900 mb-2">
+ Tone Analysis
+ </h3>
+ <div className="flex flex-wrap gap-2">
+ {tones.map((tone, idx) => (
+ <Badge key={idx} variant="outline" className="bg-white">
+ Syl {idx + 1}:{" "}
+ <span
+ className={cn("ml-1 font-bold", getColorByTone(tone))}
+ >
+ {tone}
+ </span>
+ </Badge>
+ ))}
+ </div>
+ </div>
+
+ <div className="bg-slate-100 p-4 rounded-lg border border-slate-200">
+ <h3 className="font-semibold text-slate-900 mb-2">
+ Word Structure
+ </h3>
+ <p className="text-sm text-slate-600">
+ This word consists of {syls.length} syllable
+ {syls.length > 1 ? "s" : ""}. The tone pattern is essential for
+ conveying the correct meaning.
+ </p>
+ </div>
+ </div>
+ </TabsContent>
+
+ <TabsContent value="examples" className="mt-0 space-y-4">
+ <ExamplesTab
+ word={word}
+ moreExamples={word.senses?.flatMap((s) => s.examples || [])}
+ />
+ </TabsContent>
+ </div>
+ </Tabs>
+ </div>
+</Card>;