diff --git a/package.json b/package.json index 4a72fb053..acb2e7e62 100644 --- a/package.json +++ b/package.json @@ -63,6 +63,7 @@ "react-i18next": "^11.15.3", "react-icons": "^4.3.1", "react-redux": "^7.2.6", + "react-zoom-pan-pinch": "^3.3.0", "redux": "^4.1.2", "rehype-katex": "5.0.0", "remark-math": "3.0.1", diff --git a/src/components/Layout/MDXContent.tsx b/src/components/Layout/MDXContent.tsx index 692ce12c6..7f2fb1f0a 100644 --- a/src/components/Layout/MDXContent.tsx +++ b/src/components/Layout/MDXContent.tsx @@ -18,6 +18,8 @@ import { useTotalContributors } from "components/Avatar/Contributors"; import replaceInternalHref from "utils/anchor"; import { Pre } from "components/MDXComponents/Pre"; +import { ImageZoomable } from "components/MDXComponents/ImageZoomable"; + export default function MDXContent(props: { data: any; className?: string; @@ -79,6 +81,7 @@ export default function MDXContent(props: { ...MDXConponents, Link, pre: Pre, + img: ImageZoomable, }} > {data} diff --git a/src/components/MDXComponents/ImageZoomable.tsx b/src/components/MDXComponents/ImageZoomable.tsx new file mode 100644 index 000000000..713604855 --- /dev/null +++ b/src/components/MDXComponents/ImageZoomable.tsx @@ -0,0 +1,171 @@ +import { Box, Fade, IconButton} from "@mui/material"; + +import ZoomInIcon from "@mui/icons-material/ZoomIn"; +import ZoomOutIcon from "@mui/icons-material/ZoomOut"; +import AutorenewIcon from "@mui/icons-material/Autorenew"; +import FullscreenIcon from "@mui/icons-material/Fullscreen"; +import FullscreenExitIcon from "@mui/icons-material/FullscreenExit"; + +import React, { + PropsWithChildren, + useRef, + useState, + } from "react"; +import { TransformWrapper, TransformComponent } from "react-zoom-pan-pinch"; + +type ImageZoomableWrapperProps = PropsWithChildren<{ + src: string; + alt: string; +}> + +export const ImageZoomable: React.FC = ({ src, alt }) => { + + const [isFullscreen, setIsFullScreen] = useState(false); + const [isHovered, setIsHovered] = useState(false); + const [isDragging, setIsDragging] = useState(false); + const [isTouchMoving, setTouchMoving] = useState(false); + const ImageZoomableRef = useRef(null); + const contextIconSize = isFullscreen ? ("medium") : ("small"); + var contextCursor = isDragging ? ("grabbing") : ("grab"); + + const fullscreen = () => { + if (ImageZoomableRef.current) { + if (isFullscreen) { + setIsFullScreen(false); + let ele_overlay = ImageZoomableRef.current.getElementsByClassName("zoom-overlay")[0] as HTMLElement; + ele_overlay.style.opacity = "0"; + ele_overlay.style.transition = "opacity .15s ease-in-out"; + + ImageZoomableRef.current.parentElement!.style.height = "unset"; + ImageZoomableRef.current.parentElement!.style.border = "none"; + + } else { + + setIsFullScreen(true); + let ele_overlay = ImageZoomableRef.current.getElementsByClassName("zoom-overlay")[0] as HTMLElement; + ele_overlay.style.opacity = "1"; + ele_overlay.style.transition = "opacity .3s ease-in-out"; + + ImageZoomableRef.current.parentElement!.style.height = "50px"; + ImageZoomableRef.current.parentElement!.style.border = "1px grey dashed"; + + } + } + } + + return ( + { + contextCursor = "grab"; + setIsHovered(true); + }} + onMouseLeave={() => { + if (!isFullscreen) { + setIsHovered(false); + } + }} + onMouseDown={() => { + setIsDragging(true); + contextCursor = "grabbing"; + }} + onMouseUp={() => { + setIsDragging(false); + contextCursor = "grab"; + }} + onTouchMove={() => { + setTouchMoving(true); + }} + onTouchStart={()=>{ + setTouchMoving(true); + setTimeout(() => { + setTouchMoving(false); + }, 4000); + }} + > + + + {({ zoomIn, zoomOut, resetTransform, ...rest }) => ( + + + + zoomIn()} + > + + + zoomOut()} + > + + + resetTransform()} + > + + + fullscreen()} + > + { isFullscreen ? ( + ) : ( + + ) } + + + + + {alt}/ + + + )} + + + + ); +}; diff --git a/yarn.lock b/yarn.lock index d4fb38621..f7fbe4409 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11742,6 +11742,11 @@ react-transition-group@^4.4.5: loose-envify "^1.4.0" prop-types "^15.6.2" +react-zoom-pan-pinch@^3.3.0: + version "3.3.0" + resolved "https://registry.npmmirror.com/react-zoom-pan-pinch/-/react-zoom-pan-pinch-3.3.0.tgz#873648438c5244d89fcc2127614046928429cbe0" + integrity sha512-vy1h8aenDzXye+HRqANZaSA8IPHoqOiuDPFBkswoyPUH8uMfsmbeH6gFI4r4BhEJa0xIlcA+FbvhidRWKGUrOg== + react@^17.0.2: version "17.0.2" resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037"