From 926f51aa56567c169140e2e50056b7fd3bdd1697 Mon Sep 17 00:00:00 2001 From: meetqy Date: Wed, 27 Mar 2024 15:23:24 +0800 Subject: [PATCH 1/5] frist setup --- src/app/demo/verse/index.css | 12 ++++++++++ src/app/demo/verse/page.tsx | 46 ++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 src/app/demo/verse/index.css create mode 100644 src/app/demo/verse/page.tsx diff --git a/src/app/demo/verse/index.css b/src/app/demo/verse/index.css new file mode 100644 index 00000000..91684c00 --- /dev/null +++ b/src/app/demo/verse/index.css @@ -0,0 +1,12 @@ +.py-line { + @apply mb-8 text-f200; + line-height: 2em; + + .py-chinese-item { + @apply px-1.5; + } + + rt { + @apply font-serif text-[60%] text-muted-foreground; + } +} diff --git a/src/app/demo/verse/page.tsx b/src/app/demo/verse/page.tsx new file mode 100644 index 00000000..a5f643cf --- /dev/null +++ b/src/app/demo/verse/page.tsx @@ -0,0 +1,46 @@ +"use client"; + +import { api } from "~/trpc/react"; +import "./index.css"; + +const chinese_symbols = [",", "。"]; + +export default function Page() { + const { data } = api.poem.findById.useQuery({ + id: 568, + }); + + if (!data) return null; + + const py_paragraphs = data.contentPinYin?.split("\n") ?? []; + const paragraphs = data.content.split("\n"); + + return ( +
+ {paragraphs.map((paragraph, i) => ( +

+ {paragraph.split("").map((char, j) => ( + + ))} +

+ ))} +
+ ); +} + +export const Ruby = ({ char, rt }: { char: string; rt?: string }) => { + if (rt && !chinese_symbols.includes(char)) { + return ( + + + {char} + ( + {rt} + ) + + + ); + } + + return {char}; +}; From 3a254f49c3dcb5ad5efa144509d3701c6a2d4592 Mon Sep 17 00:00:00 2001 From: meetqy Date: Wed, 27 Mar 2024 16:55:25 +0800 Subject: [PATCH 2/5] py --- src/app/demo/verse/index.css | 15 ++++++++++++-- src/app/demo/verse/page.tsx | 38 +++++++++++++++++++++++++++++++----- 2 files changed, 46 insertions(+), 7 deletions(-) diff --git a/src/app/demo/verse/index.css b/src/app/demo/verse/index.css index 91684c00..5f6bda9d 100644 --- a/src/app/demo/verse/index.css +++ b/src/app/demo/verse/index.css @@ -1,6 +1,11 @@ .py-line { - @apply mb-8 text-f200; - line-height: 2em; + @apply text-f200; + line-height: 2.5em; + text-indent: 3em; + + &:not(:last-child) { + @apply mb-8; + } .py-chinese-item { @apply px-1.5; @@ -10,3 +15,9 @@ @apply font-serif text-[60%] text-muted-foreground; } } + +.py-line.no-py { + @apply text-f200; + line-height: 1.75em; + text-indent: 2em; +} diff --git a/src/app/demo/verse/page.tsx b/src/app/demo/verse/page.tsx index a5f643cf..1d157ffb 100644 --- a/src/app/demo/verse/page.tsx +++ b/src/app/demo/verse/page.tsx @@ -2,14 +2,19 @@ import { api } from "~/trpc/react"; import "./index.css"; +import { cn } from "~/utils"; +import { useState } from "react"; +import { Button } from "~/components/ui/button"; -const chinese_symbols = [",", "。"]; +const chinese_symbols = ",。?!;:“”‘’()《》【】、".split(""); export default function Page() { const { data } = api.poem.findById.useQuery({ - id: 568, + id: 2255, }); + const [py, setPy] = useState(false); + if (!data) return null; const py_paragraphs = data.contentPinYin?.split("\n") ?? []; @@ -17,16 +22,39 @@ export default function Page() { return (
+ + +
+ ); +} + +export const Paragraph = ({ + paragraphs, + py_paragraphs, +}: { + paragraphs: string[]; + py_paragraphs: string[]; +}) => { + return ( + <> {paragraphs.map((paragraph, i) => ( -

+

{paragraph.split("").map((char, j) => ( ))}

))} - + ); -} +}; export const Ruby = ({ char, rt }: { char: string; rt?: string }) => { if (rt && !chinese_symbols.includes(char)) { From c959860751e58c8312d89afab3b6fa7721fd0e74 Mon Sep 17 00:00:00 2001 From: meetqy Date: Thu, 4 Apr 2024 20:07:00 +0800 Subject: [PATCH 3/5] article component --- src/app/demo/verse/a/page.tsx | 42 ++++++++ src/app/demo/verse/index.css | 22 +++- .../typography-article/annotation-modal.tsx | 57 ++++++++++ src/components/typography-article/index.css | 43 ++++++++ src/components/typography-article/index.tsx | 101 ++++++++++++++++++ 5 files changed, 264 insertions(+), 1 deletion(-) create mode 100644 src/app/demo/verse/a/page.tsx create mode 100644 src/components/typography-article/annotation-modal.tsx create mode 100644 src/components/typography-article/index.css create mode 100644 src/components/typography-article/index.tsx diff --git a/src/app/demo/verse/a/page.tsx b/src/app/demo/verse/a/page.tsx new file mode 100644 index 00000000..99f13a11 --- /dev/null +++ b/src/app/demo/verse/a/page.tsx @@ -0,0 +1,42 @@ +import { api } from "~/trpc/server"; +import "../index.css"; +import { useState } from "react"; +import { Button } from "~/components/ui/button"; +import { TypographyArticle } from "~/components/typography-article"; + +export default async function Page() { + const data = await api.poem.findById.query({ + id: 2255, + }); + + // const [py, setPy] = useState(false); + // const [an, setAn] = useState(true); + + const py = true; + const an = true; + + if (!data) return null; + + const py_paragraphs = data.contentPinYin?.split("\n") ?? []; + const paragraphs = data.content.split("\n"); + const annotation = ( + data.annotation ? JSON.parse(data.annotation) : {} + ) as Record; + + return ( +
+
+ {/* + */} +
+ +
+ +
+
+ ); +} diff --git a/src/app/demo/verse/index.css b/src/app/demo/verse/index.css index 5f6bda9d..0bc30634 100644 --- a/src/app/demo/verse/index.css +++ b/src/app/demo/verse/index.css @@ -11,8 +11,28 @@ @apply px-1.5; } + b { + @apply relative cursor-pointer transition-colors hover:text-destructive; + + /* &::after { + content: ""; + text-indent: 0; + + @apply absolute bottom-0 left-1/2 h-[1px] w-11/12 bg-neutral-900; + transform: translateX(-50%); + } + + &::before { + content: ""; + text-indent: 0; + + @apply absolute bottom-0.5 left-1/2 h-[1px] w-11/12 bg-neutral-900; + transform: translateX(-50%); + } */ + } + rt { - @apply font-serif text-[60%] text-muted-foreground; + @apply font-serif text-[60%] !font-normal text-muted-foreground; } } diff --git a/src/components/typography-article/annotation-modal.tsx b/src/components/typography-article/annotation-modal.tsx new file mode 100644 index 00000000..adbf7755 --- /dev/null +++ b/src/components/typography-article/annotation-modal.tsx @@ -0,0 +1,57 @@ +"use client"; + +import { useEffect } from "react"; + +export const AnnotationModal = ({ + annotation, +}: { + annotation: Record; +}) => { + useEffect(() => { + const mainBody = document.querySelector("#main-body")!; + const bodyPos = mainBody.getBoundingClientRect(); + const maxLeft = bodyPos.left + bodyPos.width - 256; + + const fn = (e: Event) => { + const target = e.target as HTMLElement; + const BTarget = + target.tagName === "B" ? target : (target.offsetParent as HTMLElement); + const Btext = BTarget.innerText.match(/[\u4e00-\u9fa5]/g)?.join("") || ""; + + if (BTarget.tagName === "B" && Btext) { + const pos = target.getBoundingClientRect(); + + const div = document.createElement("div"); + div.id = "annotation-box"; + div.setAttribute( + "style", + `position: fixed;width: 100vw;height: 100vh;top: 0;left: 0;z-index: 100;`, + ); + + div.addEventListener("click", (e) => { + const target = e.target as HTMLElement; + if (target.id === "annotation-box") { + div.remove(); + } + }); + + const x = pos.x > maxLeft ? maxLeft : pos.x; + + div.setAttribute("data-text", Btext); + div.innerHTML = `
${annotation[Btext]}
`; + + document.body.appendChild(div); + } + }; + + mainBody.addEventListener("click", fn); + + return () => { + mainBody.removeEventListener("click", fn); + }; + }, [annotation]); + + return null; +}; diff --git a/src/components/typography-article/index.css b/src/components/typography-article/index.css new file mode 100644 index 00000000..0bc30634 --- /dev/null +++ b/src/components/typography-article/index.css @@ -0,0 +1,43 @@ +.py-line { + @apply text-f200; + line-height: 2.5em; + text-indent: 3em; + + &:not(:last-child) { + @apply mb-8; + } + + .py-chinese-item { + @apply px-1.5; + } + + b { + @apply relative cursor-pointer transition-colors hover:text-destructive; + + /* &::after { + content: ""; + text-indent: 0; + + @apply absolute bottom-0 left-1/2 h-[1px] w-11/12 bg-neutral-900; + transform: translateX(-50%); + } + + &::before { + content: ""; + text-indent: 0; + + @apply absolute bottom-0.5 left-1/2 h-[1px] w-11/12 bg-neutral-900; + transform: translateX(-50%); + } */ + } + + rt { + @apply font-serif text-[60%] !font-normal text-muted-foreground; + } +} + +.py-line.no-py { + @apply text-f200; + line-height: 1.75em; + text-indent: 2em; +} diff --git a/src/components/typography-article/index.tsx b/src/components/typography-article/index.tsx new file mode 100644 index 00000000..2455d525 --- /dev/null +++ b/src/components/typography-article/index.tsx @@ -0,0 +1,101 @@ +import { cn } from "~/utils"; +import { AnnotationModal } from "./annotation-modal"; + +interface Props { + paragraphs: string[]; + py_paragraphs: string[]; + annotation: Record; +} + +export const TypographyArticle = ({ + paragraphs, + py_paragraphs, + annotation, +}: Props) => { + return ( +
+ + + +
+ ); +}; + +/** + * 段落 + */ +const Paragraph = ({ + paragraphs, + py_paragraphs, + annotation, +}: { + paragraphs: string[]; + py_paragraphs: string[]; + annotation: Record; +}) => { + return ( + <> + {paragraphs.map((paragraph, i) => ( +

+ ))} + + ); +}; + +const Annotation = ({ + paragraph, + py_paragraph, + annotation = {}, +}: { + paragraph: string; + py_paragraph?: string; + annotation: Record; +}) => { + const origin = paragraph; + const py = py_paragraph?.split(" "); + // 正则汉字 + const re = /[\u4e00-\u9fa5]/g; + const cn_symbol = /,|。|;|?|!|“|”|‘|’|(|)|《|》|【|】|、/g; + + // 中文分号替换为句号 + // 分号无法触发谷歌的首字符号优化 + paragraph = paragraph.replace( + cn_symbol, + (val) => + `${val.replace(";", "。")}`, + ); + + for (const key in annotation) { + paragraph = paragraph.replace( + new RegExp(key, "g"), + (val) => `${val}`, + ); + } + + if (!py) return paragraph; + + paragraph = paragraph.replace(re, (val) => { + const index = origin.indexOf(val); + const rt = py[index]; + + return `${val}(${rt})`; + }); + + return paragraph; +}; From 8ff0cf60b10482cc2aad4f678e1b4d784c801926 Mon Sep 17 00:00:00 2001 From: meetqy Date: Thu, 4 Apr 2024 21:20:13 +0800 Subject: [PATCH 4/5] vvv --- src/app/[lang]/poem/[id]/components/body.tsx | 26 ++++--- src/app/demo/verse/a/page.tsx | 20 +++--- src/app/demo/verse/index.css | 43 ------------ src/app/demo/verse/page.tsx | 74 -------------------- src/components/typography-article/index.css | 10 +-- src/components/typography-article/index.tsx | 1 + 6 files changed, 31 insertions(+), 143 deletions(-) delete mode 100644 src/app/demo/verse/index.css delete mode 100644 src/app/demo/verse/page.tsx diff --git a/src/app/[lang]/poem/[id]/components/body.tsx b/src/app/[lang]/poem/[id]/components/body.tsx index c5938b9e..1b2ee1d0 100644 --- a/src/app/[lang]/poem/[id]/components/body.tsx +++ b/src/app/[lang]/poem/[id]/components/body.tsx @@ -3,6 +3,7 @@ import { Verse } from "~/components/verse"; import { cn } from "~/utils"; import Link from "next/link"; import { type Locale, getLangUrl } from "~/dictionaries"; +import { TypographyArticle } from "~/components/typography-article"; export const Body = (props: { poem: Poem & { author: Author }; @@ -22,8 +23,6 @@ export const Body = (props: { const titlePinYin = py ? poem.titlePinYin ?? "" : ""; - const shi = poem.genre === "诗"; - return (
@@ -56,16 +55,23 @@ export const Body = (props: {

)} - {content.map((line, index) => ( - ( + + )) + ) : ( + - ))} + )}
); diff --git a/src/app/demo/verse/a/page.tsx b/src/app/demo/verse/a/page.tsx index 99f13a11..cc91c673 100644 --- a/src/app/demo/verse/a/page.tsx +++ b/src/app/demo/verse/a/page.tsx @@ -1,19 +1,17 @@ -import { api } from "~/trpc/server"; -import "../index.css"; +"use client"; + +import { api } from "~/trpc/react"; import { useState } from "react"; import { Button } from "~/components/ui/button"; import { TypographyArticle } from "~/components/typography-article"; -export default async function Page() { - const data = await api.poem.findById.query({ +export default function Page() { + const { data } = api.poem.findById.useQuery({ id: 2255, }); - // const [py, setPy] = useState(false); - // const [an, setAn] = useState(true); - - const py = true; - const an = true; + const [py, setPy] = useState(false); + const [an, setAn] = useState(true); if (!data) return null; @@ -26,8 +24,8 @@ export default async function Page() { return (
- {/* - */} + +
diff --git a/src/app/demo/verse/index.css b/src/app/demo/verse/index.css deleted file mode 100644 index 0bc30634..00000000 --- a/src/app/demo/verse/index.css +++ /dev/null @@ -1,43 +0,0 @@ -.py-line { - @apply text-f200; - line-height: 2.5em; - text-indent: 3em; - - &:not(:last-child) { - @apply mb-8; - } - - .py-chinese-item { - @apply px-1.5; - } - - b { - @apply relative cursor-pointer transition-colors hover:text-destructive; - - /* &::after { - content: ""; - text-indent: 0; - - @apply absolute bottom-0 left-1/2 h-[1px] w-11/12 bg-neutral-900; - transform: translateX(-50%); - } - - &::before { - content: ""; - text-indent: 0; - - @apply absolute bottom-0.5 left-1/2 h-[1px] w-11/12 bg-neutral-900; - transform: translateX(-50%); - } */ - } - - rt { - @apply font-serif text-[60%] !font-normal text-muted-foreground; - } -} - -.py-line.no-py { - @apply text-f200; - line-height: 1.75em; - text-indent: 2em; -} diff --git a/src/app/demo/verse/page.tsx b/src/app/demo/verse/page.tsx deleted file mode 100644 index 1d157ffb..00000000 --- a/src/app/demo/verse/page.tsx +++ /dev/null @@ -1,74 +0,0 @@ -"use client"; - -import { api } from "~/trpc/react"; -import "./index.css"; -import { cn } from "~/utils"; -import { useState } from "react"; -import { Button } from "~/components/ui/button"; - -const chinese_symbols = ",。?!;:“”‘’()《》【】、".split(""); - -export default function Page() { - const { data } = api.poem.findById.useQuery({ - id: 2255, - }); - - const [py, setPy] = useState(false); - - if (!data) return null; - - const py_paragraphs = data.contentPinYin?.split("\n") ?? []; - const paragraphs = data.content.split("\n"); - - return ( -
- - -
- ); -} - -export const Paragraph = ({ - paragraphs, - py_paragraphs, -}: { - paragraphs: string[]; - py_paragraphs: string[]; -}) => { - return ( - <> - {paragraphs.map((paragraph, i) => ( -

- {paragraph.split("").map((char, j) => ( - - ))} -

- ))} - - ); -}; - -export const Ruby = ({ char, rt }: { char: string; rt?: string }) => { - if (rt && !chinese_symbols.includes(char)) { - return ( - - - {char} - ( - {rt} - ) - - - ); - } - - return {char}; -}; diff --git a/src/components/typography-article/index.css b/src/components/typography-article/index.css index 0bc30634..a0847b2d 100644 --- a/src/components/typography-article/index.css +++ b/src/components/typography-article/index.css @@ -12,13 +12,13 @@ } b { - @apply relative cursor-pointer transition-colors hover:text-destructive; + @apply relative cursor-pointer font-normal text-primary transition-colors; - /* &::after { + &::after { content: ""; text-indent: 0; - @apply absolute bottom-0 left-1/2 h-[1px] w-11/12 bg-neutral-900; + @apply absolute bottom-0 left-1/2 h-[1px] w-11/12 bg-primary; transform: translateX(-50%); } @@ -26,9 +26,9 @@ content: ""; text-indent: 0; - @apply absolute bottom-0.5 left-1/2 h-[1px] w-11/12 bg-neutral-900; + @apply absolute bottom-0.5 left-1/2 h-[1px] w-11/12 bg-primary; transform: translateX(-50%); - } */ + } } rt { diff --git a/src/components/typography-article/index.tsx b/src/components/typography-article/index.tsx index 2455d525..b03097c1 100644 --- a/src/components/typography-article/index.tsx +++ b/src/components/typography-article/index.tsx @@ -1,5 +1,6 @@ import { cn } from "~/utils"; import { AnnotationModal } from "./annotation-modal"; +import "./index.css"; interface Props { paragraphs: string[]; From 364116939dc6c2a531817fe215eaa8a838ccc712 Mon Sep 17 00:00:00 2001 From: meetqy Date: Thu, 4 Apr 2024 22:11:49 +0800 Subject: [PATCH 5/5] ok --- src/app/[lang]/poem/[id]/components/body.tsx | 4 ++-- src/components/typography-article/index.css | 18 +----------------- 2 files changed, 3 insertions(+), 19 deletions(-) diff --git a/src/app/[lang]/poem/[id]/components/body.tsx b/src/app/[lang]/poem/[id]/components/body.tsx index 1b2ee1d0..66cd5f93 100644 --- a/src/app/[lang]/poem/[id]/components/body.tsx +++ b/src/app/[lang]/poem/[id]/components/body.tsx @@ -47,8 +47,8 @@ export const Body = (props: { {poem.introduce && (

{poem.introduce} diff --git a/src/components/typography-article/index.css b/src/components/typography-article/index.css index a0847b2d..17523dd7 100644 --- a/src/components/typography-article/index.css +++ b/src/components/typography-article/index.css @@ -12,23 +12,7 @@ } b { - @apply relative cursor-pointer font-normal text-primary transition-colors; - - &::after { - content: ""; - text-indent: 0; - - @apply absolute bottom-0 left-1/2 h-[1px] w-11/12 bg-primary; - transform: translateX(-50%); - } - - &::before { - content: ""; - text-indent: 0; - - @apply absolute bottom-0.5 left-1/2 h-[1px] w-11/12 bg-primary; - transform: translateX(-50%); - } + @apply relative cursor-pointer border-b border-primary/50 font-normal text-primary transition-colors dark:text-destructive; } rt {