-
Notifications
You must be signed in to change notification settings - Fork 263
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #96 from meetqy/76-诗以外的其他排版优化
76 诗以外的其他排版优化
- Loading branch information
Showing
5 changed files
with
244 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
"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 function Page() { | ||
const { data } = api.poem.findById.useQuery({ | ||
id: 2255, | ||
}); | ||
|
||
const [py, setPy] = useState(false); | ||
const [an, setAn] = useState(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<string, string>; | ||
|
||
return ( | ||
<div className="m-auto min-h-screen max-w-screen-md border py-12"> | ||
<div className="mb-12 space-x-8"> | ||
<Button onClick={() => setPy(!py)}>拼音</Button> | ||
<Button onClick={() => setAn(!an)}>标注</Button> | ||
</div> | ||
|
||
<div id="main-body" className="relative px-4"> | ||
<TypographyArticle | ||
paragraphs={paragraphs} | ||
py_paragraphs={py ? py_paragraphs : []} | ||
annotation={an ? annotation : {}} | ||
/> | ||
</div> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
"use client"; | ||
|
||
import { useEffect } from "react"; | ||
|
||
export const AnnotationModal = ({ | ||
annotation, | ||
}: { | ||
annotation: Record<string, string>; | ||
}) => { | ||
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 = `<div class="fixed w-64 z-10 text-f100 shadow-md bg-black/70 backdrop-blur-lg text-white rounded-md px-4 py-2" style="left:${x}px;top:${ | ||
pos.y + 32 | ||
}px;">${annotation[Btext]}</div>`; | ||
|
||
document.body.appendChild(div); | ||
} | ||
}; | ||
|
||
mainBody.addEventListener("click", fn); | ||
|
||
return () => { | ||
mainBody.removeEventListener("click", fn); | ||
}; | ||
}, [annotation]); | ||
|
||
return null; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
.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 border-b border-primary/50 font-normal text-primary transition-colors dark:text-destructive; | ||
} | ||
|
||
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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
import { cn } from "~/utils"; | ||
import { AnnotationModal } from "./annotation-modal"; | ||
import "./index.css"; | ||
|
||
interface Props { | ||
paragraphs: string[]; | ||
py_paragraphs: string[]; | ||
annotation: Record<string, string>; | ||
} | ||
|
||
export const TypographyArticle = ({ | ||
paragraphs, | ||
py_paragraphs, | ||
annotation, | ||
}: Props) => { | ||
return ( | ||
<div id="main-body" className="relative px-4"> | ||
<Paragraph | ||
paragraphs={paragraphs} | ||
py_paragraphs={py_paragraphs || []} | ||
annotation={annotation || {}} | ||
/> | ||
|
||
<AnnotationModal annotation={annotation} /> | ||
</div> | ||
); | ||
}; | ||
|
||
/** | ||
* 段落 | ||
*/ | ||
const Paragraph = ({ | ||
paragraphs, | ||
py_paragraphs, | ||
annotation, | ||
}: { | ||
paragraphs: string[]; | ||
py_paragraphs: string[]; | ||
annotation: Record<string, string>; | ||
}) => { | ||
return ( | ||
<> | ||
{paragraphs.map((paragraph, i) => ( | ||
<p | ||
className={cn("py-line", { | ||
"no-py": py_paragraphs.length === 0, | ||
})} | ||
key={i} | ||
dangerouslySetInnerHTML={{ | ||
__html: Annotation({ | ||
paragraph, | ||
annotation: annotation, | ||
py_paragraph: py_paragraphs[i], | ||
}), | ||
}} | ||
></p> | ||
))} | ||
</> | ||
); | ||
}; | ||
|
||
const Annotation = ({ | ||
paragraph, | ||
py_paragraph, | ||
annotation = {}, | ||
}: { | ||
paragraph: string; | ||
py_paragraph?: string; | ||
annotation: Record<string, string>; | ||
}) => { | ||
const origin = paragraph; | ||
const py = py_paragraph?.split(" "); | ||
// 正则汉字 | ||
const re = /[\u4e00-\u9fa5]/g; | ||
const cn_symbol = /,|。|;|?|!|“|”|‘|’|(|)|《|》|【|】|、/g; | ||
|
||
// 中文分号替换为句号 | ||
// 分号无法触发谷歌的首字符号优化 | ||
paragraph = paragraph.replace( | ||
cn_symbol, | ||
(val) => | ||
`<span class="py-non-chinese-item">${val.replace(";", "。")}</span>`, | ||
); | ||
|
||
for (const key in annotation) { | ||
paragraph = paragraph.replace( | ||
new RegExp(key, "g"), | ||
(val) => `<b>${val}</b>`, | ||
); | ||
} | ||
|
||
if (!py) return paragraph; | ||
|
||
paragraph = paragraph.replace(re, (val) => { | ||
const index = origin.indexOf(val); | ||
const rt = py[index]; | ||
|
||
return `<span class="py-result-item"><ruby><span class="py-chinese-item">${val}</span><rp>(</rp><rt>${rt}</rt><rp>)</rp></ruby></span>`; | ||
}); | ||
|
||
return paragraph; | ||
}; |