SentEditMenu.tsx 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. import { Button, Dropdown, Tooltip, message } from "antd";
  2. import { useState } from "react";
  3. import {
  4. EditOutlined,
  5. CopyOutlined,
  6. MoreOutlined,
  7. FieldTimeOutlined,
  8. LinkOutlined,
  9. FileMarkdownOutlined,
  10. DeleteOutlined,
  11. ReloadOutlined,
  12. } from "@ant-design/icons";
  13. import type { MenuProps } from "antd";
  14. import type { ISentence } from "../../api/sentence";
  15. import type { ArticleMode, TContentType } from "../../api/article";
  16. import {
  17. CommentOutlinedIcon,
  18. HandOutlinedIcon,
  19. JsonOutlinedIcon,
  20. MergeIcon2,
  21. PasteOutLinedIcon,
  22. } from "../../assets/icon";
  23. import { useIntl } from "react-intl";
  24. import { fullUrl } from "../../utils";
  25. import SentHistoryModal from "../sentence-history/SentHistoryModal";
  26. interface IWidget {
  27. data?: ISentence;
  28. children?: React.ReactNode;
  29. isPr?: boolean;
  30. onModeChange?: (mode: ArticleMode) => void;
  31. onConvert?: (type: TContentType) => void;
  32. onMenuClick?: (key: string) => void;
  33. }
  34. const SentEditMenuWidget = ({
  35. data,
  36. children,
  37. isPr = false,
  38. onModeChange,
  39. onConvert,
  40. onMenuClick,
  41. }: IWidget) => {
  42. const [isHover, setIsHover] = useState(false);
  43. const [timelineOpen, setTimelineOpen] = useState(false);
  44. const intl = useIntl();
  45. const onClick: MenuProps["onClick"] = (e) => {
  46. if (typeof onMenuClick !== "undefined") {
  47. onMenuClick(e.key);
  48. }
  49. switch (e.key) {
  50. case "json":
  51. if (typeof onConvert !== "undefined") {
  52. onConvert("json");
  53. }
  54. break;
  55. case "markdown":
  56. if (typeof onConvert !== "undefined") {
  57. onConvert("markdown");
  58. }
  59. break;
  60. case "timeline":
  61. setTimelineOpen(true);
  62. break;
  63. case "refresh":
  64. break;
  65. case "copy-link":
  66. if (data) {
  67. let link = `/article/para/${data.book}-${data.para}?mode=edit`;
  68. link += `&book=${data.book}&par=${data.para}`;
  69. link += `&channel=${data.channel.id}`;
  70. link += `&focus=${data.book}-${data.para}-${data.wordStart}-${data.wordEnd}`;
  71. navigator.clipboard.writeText(fullUrl(link)).then(() => {
  72. message.success("链接地址已经拷贝到剪贴板");
  73. });
  74. }
  75. break;
  76. default:
  77. break;
  78. }
  79. };
  80. const items: MenuProps["items"] = [
  81. {
  82. key: "refresh",
  83. label: intl.formatMessage({
  84. id: "buttons.refresh",
  85. }),
  86. icon: <ReloadOutlined />,
  87. },
  88. {
  89. key: "timeline",
  90. label: intl.formatMessage({
  91. id: "buttons.timeline",
  92. }),
  93. icon: <FieldTimeOutlined />,
  94. disabled: isPr,
  95. },
  96. {
  97. key: "copy-to",
  98. label: intl.formatMessage({
  99. id: "buttons.copy.to",
  100. }),
  101. icon: <MergeIcon2 />,
  102. disabled: isPr,
  103. },
  104. {
  105. type: "divider",
  106. },
  107. {
  108. key: "suggestion",
  109. label: "suggestion",
  110. icon: <HandOutlinedIcon />,
  111. disabled: isPr,
  112. },
  113. {
  114. key: "discussion",
  115. label: "discussion",
  116. icon: <CommentOutlinedIcon />,
  117. disabled: isPr,
  118. },
  119. {
  120. type: "divider",
  121. },
  122. {
  123. key: "markdown",
  124. label: "To Markdown",
  125. icon: <FileMarkdownOutlined />,
  126. disabled: !data || data.contentType === "markdown" || isPr,
  127. },
  128. {
  129. key: "json",
  130. label: "To Json",
  131. icon: <JsonOutlinedIcon />,
  132. disabled:
  133. !data ||
  134. data.channel.type !== "nissaya" ||
  135. data.contentType === "json" ||
  136. isPr,
  137. },
  138. {
  139. type: "divider",
  140. },
  141. {
  142. key: "copy-link",
  143. label: intl.formatMessage({
  144. id: "buttons.copy.link",
  145. }),
  146. icon: <LinkOutlined />,
  147. },
  148. {
  149. key: "delete",
  150. label: intl.formatMessage({
  151. id: "buttons.delete",
  152. }),
  153. icon: <DeleteOutlined />,
  154. danger: true,
  155. disabled: !isPr,
  156. },
  157. ];
  158. const buttonStyle = { backgroundColor: "rgba(1,1,1,0)", marginRight: 2 };
  159. return (
  160. <div
  161. style={{ position: "relative" }}
  162. onMouseEnter={() => {
  163. setIsHover(true);
  164. }}
  165. onMouseLeave={() => {
  166. setIsHover(false);
  167. }}
  168. >
  169. <SentHistoryModal
  170. open={timelineOpen}
  171. onClose={() => setTimelineOpen(false)}
  172. sentId={data?.id}
  173. />
  174. <div
  175. style={{
  176. marginTop: -22,
  177. right: 30,
  178. padding: 4,
  179. border: "1px solid black",
  180. borderRadius: 4,
  181. backgroundColor: "rgb(239 239 206)",
  182. position: "absolute",
  183. display: isHover ? "block" : "none",
  184. }}
  185. >
  186. <Tooltip
  187. title={intl.formatMessage({
  188. id: "buttons.edit",
  189. })}
  190. >
  191. <Button
  192. icon={<EditOutlined />}
  193. size="small"
  194. style={buttonStyle}
  195. onClick={() => {
  196. if (typeof onModeChange !== "undefined") {
  197. onModeChange("edit");
  198. }
  199. }}
  200. />
  201. </Tooltip>
  202. <Tooltip
  203. title={intl.formatMessage({
  204. id: "buttons.copy",
  205. })}
  206. >
  207. <Button
  208. icon={<CopyOutlined />}
  209. style={buttonStyle}
  210. size="small"
  211. onClick={() => {
  212. if (data?.content) {
  213. navigator.clipboard.writeText(data.content).then(() => {
  214. message.success("已经拷贝到剪贴板");
  215. });
  216. } else {
  217. message.success("内容为空");
  218. }
  219. }}
  220. />
  221. </Tooltip>
  222. <Tooltip
  223. title={intl.formatMessage({
  224. id: "buttons.paste",
  225. })}
  226. >
  227. <Button
  228. icon={<PasteOutLinedIcon />}
  229. size="small"
  230. style={buttonStyle}
  231. onClick={() => {
  232. if (typeof onMenuClick !== "undefined") {
  233. onMenuClick("paste");
  234. }
  235. }}
  236. />
  237. </Tooltip>
  238. <Dropdown
  239. disabled={data ? false : true}
  240. menu={{ items, onClick }}
  241. placement="bottomRight"
  242. >
  243. <Button icon={<MoreOutlined />} size="small" style={buttonStyle} />
  244. </Dropdown>
  245. </div>
  246. <div
  247. style={{
  248. border: isHover ? "1px solid black" : "1px solid rgba(1,1,1,0)",
  249. borderRadius: 4,
  250. }}
  251. >
  252. {children}
  253. </div>
  254. </div>
  255. );
  256. };
  257. export default SentEditMenuWidget;