Skip to content

Commit

Permalink
Merge pull request #96 from meetqy/76-诗以外的其他排版优化
Browse files Browse the repository at this point in the history
76 诗以外的其他排版优化
  • Loading branch information
meetqy authored Apr 4, 2024
2 parents afc6680 + 3641169 commit d031ef9
Show file tree
Hide file tree
Showing 5 changed files with 244 additions and 12 deletions.
30 changes: 18 additions & 12 deletions src/app/[lang]/poem/[id]/components/body.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 };
Expand All @@ -22,8 +23,6 @@ export const Body = (props: {

const titlePinYin = py ? poem.titlePinYin ?? "" : "";

const shi = poem.genre === "诗";

return (
<article className="group py-8">
<div className={cn(py && "space-y-4")}>
Expand All @@ -48,24 +47,31 @@ export const Body = (props: {
{poem.introduce && (
<p
className={cn(
"px-4 py-2 text-left text-base !not-italic text-muted-foreground transition-all md:px-0",
py && "mb-12",
"px-4 py-2 text-left !text-f50 !not-italic text-muted-foreground transition-all md:px-0",
py ? "mb-12" : "mb-4",
)}
>
{poem.introduce}
</p>
)}

{content.map((line, index) => (
<Verse
key={line}
content={line}
{isCenter ? (
content.map((line, index) => (
<Verse
key={line}
content={line}
annotation={annotation}
variant={"shi"}
py={py ? contentPinYin[index] : ""}
/>
))
) : (
<TypographyArticle
paragraphs={content}
py_paragraphs={py ? contentPinYin : []}
annotation={annotation}
variant={shi ? "shi" : isCenter ? "shi" : "body"}
py={py ? contentPinYin[index] : ""}
className={cn(!shi && "px-4 md:px-0")}
/>
))}
)}
</div>
</article>
);
Expand Down
40 changes: 40 additions & 0 deletions src/app/demo/verse/a/page.tsx
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>
);
}
57 changes: 57 additions & 0 deletions src/components/typography-article/annotation-modal.tsx
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;
};
27 changes: 27 additions & 0 deletions src/components/typography-article/index.css
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;
}
102 changes: 102 additions & 0 deletions src/components/typography-article/index.tsx
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;
};

0 comments on commit d031ef9

Please sign in to comment.