| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244 |
- import { useEffect, useMemo, useState, useCallback } from "react";
- import { Button, Dropdown, type MenuProps, Typography } from "antd";
- import { LoadingOutlined, CloseOutlined } from "@ant-design/icons";
- import { useAppSelector } from "../../hooks";
- import { settingInfo } from "../../reducers/setting";
- import { GetUserSetting } from "../auth/setting/default";
- import type { TCodeConvertor } from "./utilities";
- import {
- type ISentence,
- type IWidgetSentEditInner,
- SentEditInner,
- } from "./SentEdit";
- import MdView from "./MdView";
- import store from "../../store";
- import { push } from "../../reducers/sentence";
- import "./style.css";
- import InteractiveButton from "./SentEdit/InteractiveButton";
- import { prOpen } from "./SentEdit/SuggestionButton";
- import { openDiscussion } from "../discussion/DiscussionButton";
- import type { IEditableSentence } from "../../api/Corpus";
- import { get } from "../../request";
- const { Text } = Typography;
- const items: MenuProps["items"] = [
- { label: "编辑", key: "edit" },
- { label: "讨论", key: "discussion" },
- { label: "修改建议", key: "pr" },
- { label: "标签", key: "tag" },
- ];
- interface IWidgetSentReadFrame {
- origin?: ISentence[];
- translation?: ISentence[];
- layout?: "row" | "column";
- book?: number;
- para?: number;
- wordStart?: number;
- wordEnd?: number;
- sentId?: string;
- error?: string;
- }
- const SentReadFrame = ({
- origin,
- translation,
- book,
- para,
- wordStart,
- wordEnd,
- error,
- }: IWidgetSentReadFrame) => {
- const settings = useAppSelector(settingInfo);
- const [loadingId, setLoadingId] = useState<string | null>(null);
- const [active, setActive] = useState(false);
- const [sentData, setSentData] = useState<IWidgetSentEditInner>();
- const [showEdit, setShowEdit] = useState(false);
- /** 派生数据:主巴利编码 */
- const paliCode = useMemo(() => {
- const v = GetUserSetting("setting.pali.script.primary", settings);
- return (v ?? "roman") as TCodeConvertor;
- }, [settings]);
- /** 派生数据:是否显示原文 */
- const displayOriginal = useMemo(() => {
- return GetUserSetting("setting.display.original", settings);
- }, [settings]);
- /** 派生数据:布局方向 */
- const layoutDirection = useMemo<React.CSSProperties["flexDirection"]>(() => {
- const v = GetUserSetting("setting.layout.direction", settings);
- if (
- v === "row" ||
- v === "column" ||
- v === "row-reverse" ||
- v === "column-reverse"
- ) {
- return v;
- }
- return "row";
- }, [settings]);
- /** push 到 store(副作用) */
- useEffect(() => {
- store.dispatch(
- push({
- id: `${book}-${para}-${wordStart}-${wordEnd}`,
- origin: origin?.map((item) => item.html),
- translation: translation?.map((item) => item.html),
- })
- );
- }, [book, origin, para, translation, wordEnd, wordStart]);
- /** 菜单点击 */
- const handleMenuClick = useCallback(async (key: string, item: ISentence) => {
- switch (key) {
- case "edit":
- if (!item.id) return;
- setLoadingId(item.id);
- try {
- const json = await get<IEditableSentence>(
- `/v2/editable-sentence/${item.id}`
- );
- if (json.ok) {
- setSentData(json.data);
- setShowEdit(true);
- }
- } finally {
- setLoadingId(null);
- }
- break;
- case "discussion":
- if (item.id) {
- openDiscussion(item.id, "sentence", false);
- }
- break;
- case "pr":
- prOpen(item);
- break;
- }
- }, []);
- return (
- <span
- className="sent_read_shell"
- style={{ flexDirection: layoutDirection }}
- >
- <Text type="danger" mark>
- {error}
- </Text>
- {/* anchor */}
- <span
- dangerouslySetInnerHTML={{
- __html: `<span class="pcd_sent" id="sent_${book}-${para}-${wordStart}-${wordEnd}"></span>`,
- }}
- />
- {/* 原文 */}
- <span
- style={{
- flex: 5,
- color: "#9f3a01",
- display:
- displayOriginal === false && translation?.length ? "none" : "block",
- }}
- >
- {origin?.map((item, id) => (
- <Text key={id}>
- <MdView
- style={{ color: "brown" }}
- html={item.html}
- wordWidget
- convertor={paliCode}
- />
- </Text>
- ))}
- </span>
- {/* 译文 */}
- <span className="sent_read" style={{ flex: 5 }}>
- {translation?.map((item, id) => (
- <span key={id}>
- {loadingId === item.id && <LoadingOutlined />}
- <Dropdown
- trigger={["contextMenu"]}
- menu={{
- items,
- onClick: (e) => handleMenuClick(e.key, item),
- }}
- >
- <Text
- className="sent_read_translation"
- style={{ display: showEdit ? "none" : "inline" }}
- >
- <MdView
- html={item.html}
- style={{ backgroundColor: active ? "beige" : undefined }}
- />
- </Text>
- </Dropdown>
- {/* 编辑面板 */}
- {showEdit && (
- <div>
- <div style={{ textAlign: "right" }}>
- <Button
- size="small"
- icon={<CloseOutlined />}
- onClick={() => setShowEdit(false)}
- >
- 返回审阅模式
- </Button>
- </div>
- {sentData ? (
- <SentEditInner
- mode="edit"
- {...sentData}
- onTranslationChange={(data: ISentence) => {
- if (!translation) return;
- const copy = [...translation];
- copy[id] = data;
- }}
- />
- ) : (
- "无数据"
- )}
- </div>
- )}
- <InteractiveButton
- data={item}
- compact
- float
- hideCount
- hideInZero
- onMouseEnter={() => setActive(true)}
- onMouseLeave={() => setActive(false)}
- />
- </span>
- ))}
- </span>
- </span>
- );
- };
- interface IWidget {
- props: string;
- }
- const Widget = ({ props }: IWidget) => {
- const prop = JSON.parse(atob(props)) as IWidgetSentReadFrame;
- return <SentReadFrame {...prop} />;
- };
- export default Widget;
|