previewFooter.tsx 9.7 KB

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