diff options
Diffstat (limited to 'packages/prosody-ui/src/components/word/FullWordData.tsx')
| -rw-r--r-- | packages/prosody-ui/src/components/word/FullWordData.tsx | 156 |
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>; |
