previewFooter.tsx 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. import React, { ReactNode } from "react";
  2. import BaseComponent from "../_base/baseComponent";
  3. import { IconChevronLeft, IconChevronRight, IconMinus, IconPlus, IconRotate, IconDownload, IconWindowAdaptionStroked, IconRealSizeStroked, IconSize } from "@douyinfe/semi-icons";
  4. import { FooterProps } from "./interface";
  5. import PropTypes from "prop-types";
  6. import Tooltip from "../tooltip";
  7. import Divider from "../divider";
  8. import Slider from "../slider";
  9. import Icon from "../icons";
  10. import { cssClasses } from "@douyinfe/semi-foundation/image/constants";
  11. import cls from "classnames";
  12. import PreviewFooterFoundation, { PreviewFooterAdapter } from "@douyinfe/semi-foundation/image/previewFooterFoundation";
  13. import LocaleConsumer from "../locale/localeConsumer";
  14. import { Locale } from "../locale/interface";
  15. import { throttle } from "lodash";
  16. const prefixCls = cssClasses.PREFIX;
  17. const footerPrefixCls = `${cssClasses.PREFIX}-preview-footer`;
  18. let mouseActiveTime: number = 0;
  19. export default class Footer extends BaseComponent<FooterProps> {
  20. static propTypes = {
  21. curPage: PropTypes.number,
  22. totalNum: PropTypes.number,
  23. disabledPrev: PropTypes.bool,
  24. disabledNext: PropTypes.bool,
  25. disableDownload: PropTypes.bool,
  26. className: PropTypes.string,
  27. zoom: PropTypes.number,
  28. ratio: PropTypes.string,
  29. prevTip: PropTypes.string,
  30. nextTip: PropTypes.string,
  31. zoomInTip: PropTypes.string,
  32. zoomOutTip: PropTypes.string,
  33. rotateTip: PropTypes.string,
  34. downloadTip: PropTypes.string,
  35. adaptiveTip: PropTypes.string,
  36. originTip: PropTypes.string,
  37. showTooltip: PropTypes.bool,
  38. onZoomIn: PropTypes.func,
  39. onZoomOut: PropTypes.func,
  40. onPrev: PropTypes.func,
  41. onNext: PropTypes.func,
  42. onAdjustRatio: PropTypes.func,
  43. onRotateLeft: PropTypes.func,
  44. onDownload: PropTypes.func,
  45. }
  46. static defaultProps = {
  47. min: 10,
  48. max: 500,
  49. step: 10,
  50. showTooltip: false,
  51. disableDownload: false,
  52. }
  53. get adapter(): PreviewFooterAdapter<FooterProps> {
  54. return {
  55. ...super.adapter,
  56. setStartMouseOffset: (time: number) => {
  57. mouseActiveTime = time;
  58. }
  59. };
  60. }
  61. foundation: PreviewFooterFoundation;
  62. constructor(props: FooterProps) {
  63. super(props);
  64. this.foundation = new PreviewFooterFoundation(this.adapter);
  65. }
  66. changeSliderValue = (type: string): void => {
  67. this.foundation.changeSliderValue(type);
  68. };
  69. handleMinusClick = () => {
  70. this.changeSliderValue("minus");
  71. }
  72. handlePlusClick = () => {
  73. this.changeSliderValue("plus");
  74. }
  75. handleRotateLeft = () => {
  76. this.foundation.handleRotate("left");
  77. }
  78. handleRotateRight = () => {
  79. this.foundation.handleRotate("right");
  80. }
  81. handleSlideChange = throttle((value): void => {
  82. this.foundation.handleValueChange(value);
  83. }, 50);
  84. handleRatioClick = (): void => {
  85. this.foundation.handleRatioClick();
  86. }
  87. customRenderViewMenu = (): ReactNode => {
  88. const { min, max, step, curPage, totalNum, ratio, zoom, disabledPrev, disabledNext,
  89. disableDownload, onNext, onPrev, onDownload, renderPreviewMenu }
  90. = this.props;
  91. const props = { min, max, step, curPage, totalNum, ratio, zoom,
  92. disabledPrev, disabledNext, disableDownload, onNext, onPrev, onDownload,
  93. onRotateLeft: this.handleRotateLeft,
  94. onRotateRight: this.handleRotateRight,
  95. disabledZoomIn: zoom === max,
  96. disabledZoomOut: zoom === min,
  97. onRatioClick: this.handleRatioClick,
  98. onZoomIn: this.handlePlusClick,
  99. onZoomOut: this.handleMinusClick,
  100. };
  101. return renderPreviewMenu(props);
  102. }
  103. // According to showTooltip in props, decide whether to use Tooltip to pack a layer
  104. // 根据 props 中的 showTooltip 决定是否使用 Tooltip 包一层
  105. getFinalIconElement = (element: ReactNode, content: ReactNode) => {
  106. const { showTooltip } = this.props;
  107. return showTooltip ? (
  108. <Tooltip content={content}>
  109. {element}
  110. </Tooltip>
  111. ): element;
  112. }
  113. getLocalTextByKey = (key: string) => (
  114. <LocaleConsumer<Locale["Image"]> componentName="Image" >
  115. {(locale: Locale["Image"]) => locale[key]}
  116. </LocaleConsumer>
  117. );
  118. getIconChevronLeft = () => {
  119. const { disabledPrev, onPrev, prevTip } = this.props;
  120. const icon = <IconChevronLeft
  121. size="large"
  122. className={disabledPrev ? `${footerPrefixCls}-disabled` : ""}
  123. onClick={!disabledPrev ? onPrev : undefined}
  124. />;
  125. const content = prevTip ?? this.getLocalTextByKey("prevTip");
  126. return this.getFinalIconElement(icon, content);
  127. }
  128. getIconChevronRight = () => {
  129. const { disabledNext, onNext, nextTip } = this.props;
  130. const icon = <IconChevronRight
  131. size="large"
  132. className={disabledNext ? `${footerPrefixCls}-disabled` : ""}
  133. onClick={!disabledNext ? onNext : undefined}
  134. />;
  135. const content = nextTip ?? this.getLocalTextByKey("nextTip");
  136. return this.getFinalIconElement(icon, content);
  137. }
  138. getIconMinus = () => {
  139. const { zoomOutTip, zoom, min } = this.props;
  140. const disabledZoomOut = zoom === min;
  141. const icon = <IconMinus
  142. size="large"
  143. onClick={!disabledZoomOut ? this.handleMinusClick : undefined}
  144. className={disabledZoomOut ? `${footerPrefixCls}-disabled` : ""}
  145. />;
  146. const content = zoomOutTip ?? this.getLocalTextByKey("zoomOutTip");
  147. return this.getFinalIconElement(icon, content);
  148. }
  149. getIconPlus = () => {
  150. const { zoomInTip, zoom, max } = this.props;
  151. const disabledZoomIn = zoom === max;
  152. const icon = <IconPlus
  153. size="large"
  154. onClick={!disabledZoomIn ? this.handlePlusClick : undefined}
  155. className={disabledZoomIn ? `${footerPrefixCls}-disabled` : ""}
  156. />;
  157. const content = zoomInTip ?? this.getLocalTextByKey("zoomInTip");
  158. return this.getFinalIconElement(icon, content);
  159. }
  160. getIconRatio = () => {
  161. const { ratio, originTip, adaptiveTip } = this.props;
  162. const props = {
  163. size: "large" as IconSize,
  164. className: cls(`${footerPrefixCls}-gap`),
  165. onClick: this.handleRatioClick,
  166. };
  167. const icon = ratio === "adaptation" ? <IconRealSizeStroked {...props} /> : <IconWindowAdaptionStroked {...props} />;
  168. let content: any;
  169. if (ratio === "adaptation") {
  170. content = originTip ?? this.getLocalTextByKey("originTip");
  171. } else {
  172. content = adaptiveTip ?? this.getLocalTextByKey("adaptiveTip");
  173. }
  174. return this.getFinalIconElement(icon, content);
  175. }
  176. getIconRotate = () => {
  177. const { rotateTip } = this.props;
  178. const icon = <IconRotate
  179. size="large"
  180. onClick={this.handleRotateLeft}
  181. />;
  182. const content = rotateTip ?? this.getLocalTextByKey("rotateTip");
  183. return this.getFinalIconElement(icon, content);
  184. }
  185. getIconDownload = () => {
  186. const { downloadTip, onDownload, disableDownload } = this.props;
  187. const icon = <IconDownload
  188. size="large"
  189. onClick={!disableDownload ? onDownload : undefined}
  190. className={cls(`${footerPrefixCls}-gap`,
  191. {
  192. [`${footerPrefixCls}-disabled`] : disableDownload,
  193. },
  194. )}
  195. />;
  196. const content = downloadTip ?? this.getLocalTextByKey("downloadTip");
  197. return this.getFinalIconElement(icon, content);
  198. }
  199. render() {
  200. const {
  201. min,
  202. max,
  203. step,
  204. curPage,
  205. totalNum,
  206. zoom,
  207. showTooltip,
  208. className,
  209. renderPreviewMenu,
  210. } = this.props;
  211. if (renderPreviewMenu) {
  212. return (
  213. <div className={`${footerPrefixCls}-wrapper`}>
  214. {this.customRenderViewMenu()}
  215. </div>
  216. );
  217. }
  218. return (
  219. <section className={cls(footerPrefixCls, `${footerPrefixCls}-wrapper`, className)}>
  220. {this.getIconChevronLeft()}
  221. <div className={`${footerPrefixCls}-page`}>
  222. <span>{curPage}</span><span>/</span><span>{totalNum}</span>
  223. </div>
  224. {this.getIconChevronRight()}
  225. <Divider layout="vertical" />
  226. {this.getIconMinus()}
  227. <Slider
  228. value={zoom}
  229. min={min}
  230. max={max}
  231. step={step}
  232. tipFormatter={(v): string => `${v}%`}
  233. tooltipVisible={showTooltip ? undefined : false }
  234. onChange={this.handleSlideChange}
  235. />
  236. {this.getIconPlus()}
  237. {this.getIconRatio()}
  238. <Divider layout="vertical" />
  239. {this.getIconRotate()}
  240. {this.getIconDownload()}
  241. </section>
  242. );
  243. }
  244. }