SentTab.tsx 11 KB

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