NavigateButton.tsx 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. import { Button, Dropdown, Modal, Space, Typography } from "antd";
  2. import { DoubleRightOutlined, DoubleLeftOutlined } from "@ant-design/icons";
  3. import { FolderOutlined } from "@ant-design/icons";
  4. import type { ITocPathNode } from "../corpus/TocPath"
  5. const { Paragraph, Text } = Typography;
  6. const EllipsisMiddle: React.FC<{
  7. suffixCount: number;
  8. maxWidth: number;
  9. text?: string;
  10. }> = ({ suffixCount, maxWidth = 500, text = "" }) => {
  11. const start = text.slice(0, text.length - suffixCount).trim();
  12. const suffix = text.slice(-suffixCount).trim();
  13. return (
  14. <Text style={{ maxWidth: maxWidth }} ellipsis={{ suffix }}>
  15. {start}
  16. </Text>
  17. );
  18. };
  19. interface IWidget {
  20. prevTitle?: string;
  21. nextTitle?: string;
  22. path?: ITocPathNode[];
  23. topOfChapter?: boolean;
  24. endOfChapter?: boolean;
  25. onPrev?: Function;
  26. onNext?: Function;
  27. onPathChange?: Function;
  28. }
  29. const NavigateButtonWidget = ({
  30. prevTitle,
  31. nextTitle,
  32. topOfChapter = false,
  33. endOfChapter = false,
  34. path,
  35. onPrev,
  36. onNext,
  37. onPathChange,
  38. }: IWidget) => {
  39. const currTitle = path && path.length > 0 ? path[path.length - 1].title : "";
  40. return (
  41. <Paragraph
  42. style={{
  43. display: "flex",
  44. justifyContent: "space-between",
  45. backdropFilter: "blur(5px)",
  46. backgroundColor: "rgba(200,200,200,0.2)",
  47. padding: 4,
  48. }}
  49. >
  50. <Button
  51. size="middle"
  52. icon={topOfChapter ? <FolderOutlined /> : undefined}
  53. disabled={typeof prevTitle === "undefined"}
  54. onClick={(event: React.MouseEvent<HTMLElement, MouseEvent>) => {
  55. if (typeof onPrev !== "undefined") {
  56. if (topOfChapter) {
  57. Modal.confirm({
  58. content: "已经到达章节开头,去上一个章节吗?",
  59. okText: "确认",
  60. cancelText: "取消",
  61. onOk: () => {
  62. onPrev(event);
  63. },
  64. });
  65. } else {
  66. onPrev(event);
  67. }
  68. }
  69. }}
  70. >
  71. <Space>
  72. <DoubleLeftOutlined key="icon" />
  73. <EllipsisMiddle maxWidth={250} suffixCount={7} text={prevTitle} />
  74. </Space>
  75. </Button>
  76. <div>
  77. <Dropdown
  78. placement="top"
  79. trigger={["hover"]}
  80. menu={{
  81. items: path?.map((item, _id) => {
  82. return { label: item.title, key: item.key ?? item.title };
  83. }),
  84. onClick: (e) => {
  85. console.debug("onPathChange", e.key);
  86. if (typeof onPathChange !== "undefined") {
  87. onPathChange(e.key);
  88. }
  89. },
  90. }}
  91. >
  92. <span>{currTitle}</span>
  93. </Dropdown>
  94. </div>
  95. <Button
  96. icon={endOfChapter ? <FolderOutlined /> : undefined}
  97. size="middle"
  98. disabled={typeof nextTitle === "undefined"}
  99. onClick={(event: React.MouseEvent<HTMLElement, MouseEvent>) => {
  100. if (typeof onNext !== "undefined") {
  101. if (endOfChapter) {
  102. Modal.confirm({
  103. content: "已经到达章节末尾,去下一个章节吗?",
  104. okText: "确认",
  105. cancelText: "取消",
  106. onOk: () => {
  107. onNext(event);
  108. },
  109. });
  110. } else {
  111. onNext(event);
  112. }
  113. }
  114. }}
  115. >
  116. <Space>
  117. <EllipsisMiddle
  118. key="title"
  119. maxWidth={250}
  120. suffixCount={7}
  121. text={nextTitle?.substring(0, 20)}
  122. />
  123. <DoubleRightOutlined key="icon" />
  124. </Space>
  125. </Button>
  126. </Paragraph>
  127. );
  128. };
  129. export default NavigateButtonWidget;