SentTab.tsx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  1. import { useEffect, useState } from "react";
  2. import { Badge, Space, Tabs, Typography, message } from "antd";
  3. import {
  4. TranslationOutlined,
  5. CloseOutlined,
  6. BlockOutlined,
  7. } from "@ant-design/icons";
  8. import SentTabButton from "./SentTabButton";
  9. import SentCanRead from "./SentCanRead";
  10. import SentSim from "./SentSim";
  11. import { useIntl } from "react-intl";
  12. import TocPath from "../../../src/components/tipitaka/TocPath";
  13. import SentMenu from "./SentMenu";
  14. import SentTabCopy from "./SentTabCopy";
  15. import { fullUrl } from "../../utils";
  16. import SentWbw from "./SentWbw";
  17. import SentTabButtonWbw from "./SentTabButtonWbw";
  18. import type { IWbw } from "../../types/wbw";
  19. import type { IResNumber } from "../../api/channel";
  20. import RelaGraphic from "../wbw/RelaGraphic";
  21. import type { ITocPathNode } from "../../api/pali-text";
  22. import type { ArticleMode } from "../../api/article";
  23. import type { ISentence } from "../../api/sentence";
  24. const { Text } = Typography;
  25. interface IWidget {
  26. id: string;
  27. book: number;
  28. para: number;
  29. wordStart: number;
  30. wordEnd: number;
  31. channelsId?: string[];
  32. path?: ITocPathNode[];
  33. layout?: "row" | "column";
  34. tranNum?: number;
  35. nissayaNum?: number;
  36. commNum?: number;
  37. originNum: number;
  38. simNum?: number;
  39. wbwData?: IWbw[];
  40. magicDictLoading?: boolean;
  41. compact?: boolean;
  42. mode?: ArticleMode;
  43. loadedRes?: IResNumber;
  44. origin?: ISentence[];
  45. onMagicDict?: (type: string) => void;
  46. onCompact?: (compact: boolean) => void;
  47. onModeChange?: (mode: ArticleMode) => void;
  48. onAffix?: () => void;
  49. }
  50. const SentTabWidget = ({
  51. id,
  52. book,
  53. para,
  54. wordStart,
  55. wordEnd,
  56. channelsId,
  57. path,
  58. tranNum = 0,
  59. nissayaNum = 0,
  60. commNum = 0,
  61. originNum,
  62. simNum = 0,
  63. wbwData,
  64. magicDictLoading = false,
  65. compact = false,
  66. mode,
  67. loadedRes,
  68. origin,
  69. onMagicDict,
  70. onCompact,
  71. onModeChange,
  72. onAffix,
  73. }: IWidget) => {
  74. const intl = useIntl();
  75. const [isCompact, setIsCompact] = useState(compact);
  76. const [hover, setHover] = useState(false);
  77. const [currKey, setCurrKey] = useState("close");
  78. const [currTranNum, setCurrTranNum] = useState(tranNum);
  79. const [currNissayaNum, setCurrNissayaNum] = useState(nissayaNum);
  80. const [currCommNum, setCurrCommNum] = useState(commNum);
  81. const [currSimilarNum, setCurrSimilarNum] = useState(simNum);
  82. const [showWbwProgress, setShowWbwProgress] = useState(false);
  83. console.log("SentTabWidget render");
  84. useEffect(() => {
  85. setIsCompact(compact);
  86. }, [compact]);
  87. const mPath = path
  88. ? [
  89. ...path,
  90. { book: book, paragraph: para, title: para.toString(), level: 100 },
  91. ]
  92. : [];
  93. if (typeof id === "undefined") {
  94. return <></>;
  95. }
  96. const sentId = id.split("_");
  97. const sId = sentId[0].split("-");
  98. const tabButtonStyle: React.CSSProperties | undefined = compact
  99. ? { visibility: hover || currKey !== "close" ? "visible" : "hidden" }
  100. : undefined;
  101. return (
  102. <Tabs
  103. className={
  104. "sent_tabs" +
  105. (isCompact ? " compact" : "") +
  106. (currKey === "close" ? " curr_close" : "")
  107. }
  108. onMouseEnter={() => setHover(true)}
  109. onMouseLeave={() => setHover(false)}
  110. activeKey={currKey}
  111. type="card"
  112. onChange={(activeKey: string) => {
  113. setCurrKey(activeKey);
  114. }}
  115. tabBarStyle={{ marginBottom: 0 }}
  116. size="small"
  117. tabBarGutter={0}
  118. tabBarExtraContent={
  119. <Space>
  120. <TocPath
  121. link="none"
  122. data={mPath}
  123. channels={channelsId}
  124. trigger={path ? path.length > 0 ? path[0].title : <></> : <></>}
  125. />
  126. <Text>{sentId[0]}</Text>
  127. <SentTabCopy wbwData={wbwData} text={`{{${sentId[0]}}}`} />
  128. <SentMenu
  129. book={book}
  130. para={para}
  131. loading={magicDictLoading}
  132. mode={mode}
  133. onMagicDict={(type: string) => {
  134. if (typeof onMagicDict !== "undefined") {
  135. onMagicDict(type);
  136. }
  137. }}
  138. onMenuClick={(key: string) => {
  139. switch (key) {
  140. case "compact":
  141. if (typeof onCompact !== "undefined") {
  142. setIsCompact(true);
  143. onCompact(true);
  144. }
  145. break;
  146. case "normal":
  147. if (typeof onCompact !== "undefined") {
  148. setIsCompact(false);
  149. onCompact(false);
  150. }
  151. break;
  152. case "origin-edit":
  153. if (typeof onModeChange !== "undefined") {
  154. onModeChange("edit");
  155. }
  156. break;
  157. case "origin-wbw":
  158. if (typeof onModeChange !== "undefined") {
  159. onModeChange("wbw");
  160. }
  161. break;
  162. case "copy-id": {
  163. const id = `{{${book}-${para}-${wordStart}-${wordEnd}}}`;
  164. navigator.clipboard.writeText(id).then(() => {
  165. message.success("编号已经拷贝到剪贴板");
  166. });
  167. break;
  168. }
  169. case "copy-link": {
  170. let link = `/article/para/${book}-${para}?mode=edit`;
  171. link += `&book=${book}&par=${para}`;
  172. if (channelsId) {
  173. link += `&channel=` + channelsId?.join("_");
  174. }
  175. link += `&focus=${book}-${para}-${wordStart}-${wordEnd}`;
  176. navigator.clipboard.writeText(fullUrl(link)).then(() => {
  177. message.success("链接地址已经拷贝到剪贴板");
  178. });
  179. break;
  180. }
  181. case "affix":
  182. if (typeof onAffix !== "undefined") {
  183. onAffix();
  184. }
  185. break;
  186. default:
  187. break;
  188. }
  189. }}
  190. />
  191. </Space>
  192. }
  193. items={[
  194. {
  195. label: (
  196. <span style={tabButtonStyle}>
  197. <Badge size="small" count={0}>
  198. <CloseOutlined />
  199. </Badge>
  200. </span>
  201. ),
  202. key: "close",
  203. children: <></>,
  204. },
  205. {
  206. label: (
  207. <SentTabButton
  208. style={tabButtonStyle}
  209. icon={<TranslationOutlined />}
  210. type="translation"
  211. sentId={id}
  212. count={
  213. currTranNum
  214. ? currTranNum -
  215. (loadedRes?.translation ? loadedRes.translation : 0)
  216. : undefined
  217. }
  218. title={intl.formatMessage({
  219. id: "channel.type.translation.label",
  220. })}
  221. />
  222. ),
  223. key: "translation",
  224. children: (
  225. <div className="content">
  226. <SentCanRead
  227. book={parseInt(sId[0])}
  228. para={parseInt(sId[1])}
  229. wordStart={parseInt(sId[2])}
  230. wordEnd={parseInt(sId[3])}
  231. type="translation"
  232. channelsId={channelsId}
  233. onCreate={() => setCurrTranNum((origin) => origin + 1)}
  234. />
  235. </div>
  236. ),
  237. },
  238. {
  239. label: (
  240. <SentTabButton
  241. style={tabButtonStyle}
  242. icon={<CloseOutlined />}
  243. type="nissaya"
  244. sentId={id}
  245. count={
  246. currNissayaNum
  247. ? currNissayaNum -
  248. (loadedRes?.nissaya ? loadedRes.nissaya : 0)
  249. : undefined
  250. }
  251. title={intl.formatMessage({
  252. id: "channel.type.nissaya.label",
  253. })}
  254. />
  255. ),
  256. key: "nissaya",
  257. children: (
  258. <SentCanRead
  259. book={parseInt(sId[0])}
  260. para={parseInt(sId[1])}
  261. wordStart={parseInt(sId[2])}
  262. wordEnd={parseInt(sId[3])}
  263. type="nissaya"
  264. channelsId={channelsId}
  265. onCreate={() => setCurrNissayaNum((origin) => origin + 1)}
  266. />
  267. ),
  268. },
  269. {
  270. label: (
  271. <SentTabButton
  272. style={tabButtonStyle}
  273. icon={<TranslationOutlined />}
  274. type="commentary"
  275. sentId={id}
  276. count={
  277. currCommNum
  278. ? currCommNum -
  279. (loadedRes?.commentary ? loadedRes.commentary : 0)
  280. : undefined
  281. }
  282. title={intl.formatMessage({
  283. id: "channel.type.commentary.label",
  284. })}
  285. />
  286. ),
  287. key: "commentary",
  288. children: (
  289. <SentCanRead
  290. book={parseInt(sId[0])}
  291. para={parseInt(sId[1])}
  292. wordStart={parseInt(sId[2])}
  293. wordEnd={parseInt(sId[3])}
  294. type="commentary"
  295. channelsId={channelsId}
  296. onCreate={() => setCurrCommNum((origin) => origin + 1)}
  297. />
  298. ),
  299. },
  300. {
  301. label: (
  302. <SentTabButton
  303. icon={<BlockOutlined />}
  304. type="original"
  305. sentId={id}
  306. count={originNum}
  307. title={intl.formatMessage({
  308. id: "channel.type.original.label",
  309. })}
  310. />
  311. ),
  312. key: "original",
  313. children: (
  314. <SentCanRead
  315. book={parseInt(sId[0])}
  316. para={parseInt(sId[1])}
  317. wordStart={parseInt(sId[2])}
  318. wordEnd={parseInt(sId[3])}
  319. type="original"
  320. origin={origin}
  321. />
  322. ),
  323. },
  324. {
  325. label: (
  326. <SentTabButton
  327. style={tabButtonStyle}
  328. icon={<BlockOutlined />}
  329. type="original"
  330. sentId={id}
  331. count={currSimilarNum}
  332. title={intl.formatMessage({
  333. id: "buttons.sim",
  334. })}
  335. />
  336. ),
  337. key: "sim",
  338. children: (
  339. <SentSim
  340. book={parseInt(sId[0])}
  341. para={parseInt(sId[1])}
  342. wordStart={parseInt(sId[2])}
  343. wordEnd={parseInt(sId[3])}
  344. channelsId={channelsId}
  345. limit={5}
  346. onCreate={() => setCurrSimilarNum((origin) => origin + 1)}
  347. />
  348. ),
  349. },
  350. {
  351. label: (
  352. <SentTabButtonWbw
  353. style={tabButtonStyle}
  354. sentId={id}
  355. count={0}
  356. onMenuClick={(keyPath: string[]) => {
  357. switch (keyPath.join("-")) {
  358. case "show-progress":
  359. setShowWbwProgress((origin) => !origin);
  360. break;
  361. }
  362. }}
  363. />
  364. ),
  365. key: "wbw",
  366. children: (
  367. <SentWbw
  368. book={parseInt(sId[0])}
  369. para={parseInt(sId[1])}
  370. wordStart={parseInt(sId[2])}
  371. wordEnd={parseInt(sId[3])}
  372. channelsId={channelsId}
  373. wbwProgress={showWbwProgress}
  374. />
  375. ),
  376. },
  377. {
  378. label: <span style={tabButtonStyle}>{"关系图"}</span>,
  379. key: "relation-graphic",
  380. children: <RelaGraphic wbwData={wbwData} />,
  381. },
  382. ]}
  383. />
  384. );
  385. };
  386. export default SentTabWidget;