150 lines
4.5 KiB
TypeScript
Executable File
150 lines
4.5 KiB
TypeScript
Executable File
import React, { useEffect, useRef, useState } from "react";
|
|
import manageJss, { ComponentStyles } from "@microsoft/fast-jss-manager-react";
|
|
import { FileViewClassNameContract, FileViewProps } from "./FileView.props";
|
|
import {
|
|
neutralForegroundRest,
|
|
DesignSystem,
|
|
neutralLayerL1,
|
|
} from "@microsoft/fast-components-styles-msft";
|
|
import { motion } from "framer-motion";
|
|
import { Header } from "../_DesignSystem";
|
|
import { HeaderClassNameContract } from "../_DesignSystem/Header/Header.props";
|
|
import ImageViewer from "./views/ImageViewer/ImageViewer";
|
|
import { HeaderCenterContent } from "./views/HeaderContent/HeaderCenterContent";
|
|
import { HeaderRightContent } from "./views/HeaderContent/HeaderRightContent";
|
|
import { FVSidebarProps } from "./views/FVSidebar/FVSidebar.props";
|
|
import { LoadableComponent } from "@loadable/component";
|
|
import { FullscreenLoader } from "../Loader";
|
|
import FVSidebarProvider from "./views/FVSidebar/FVSidebarContext";
|
|
import FVSidebarToggleButton from "./views/FVSidebar/FVSidebarToggleButton";
|
|
|
|
const FVSidebar: LoadableComponent<FVSidebarProps> = FullscreenLoader(
|
|
import(/* webpackChunkName: "FVSidebar" */ "./views/FVSidebar/FVSidebar")
|
|
);
|
|
|
|
const styles: ComponentStyles<FileViewClassNameContract, DesignSystem> = {
|
|
fileView: {
|
|
display: "flex",
|
|
position: "fixed",
|
|
zIndex: "30",
|
|
top: "0",
|
|
left: "0",
|
|
minWidth: "100%",
|
|
minHeight: "100%",
|
|
},
|
|
fileViewBackground: {
|
|
position: "absolute",
|
|
width: "100%",
|
|
height: "100%",
|
|
top: "0",
|
|
left: "0",
|
|
background: neutralLayerL1,
|
|
},
|
|
fileViewContainer: {
|
|
position: "absolute",
|
|
top: "64px",
|
|
left: "0",
|
|
right: "0",
|
|
bottom: "0",
|
|
margin: "auto",
|
|
cursor: "zoom-in",
|
|
},
|
|
fileViewIcon: {
|
|
fontSize: "4em",
|
|
color: (designSystem: DesignSystem): string => neutralForegroundRest(designSystem),
|
|
},
|
|
};
|
|
|
|
const headerStyles: ComponentStyles<HeaderClassNameContract, DesignSystem> = {
|
|
header: {
|
|
zIndex: "40",
|
|
},
|
|
header_left: {},
|
|
};
|
|
|
|
let largeImage: HTMLImageElement = null;
|
|
|
|
const FileView: React.FC<FileViewProps> = ({
|
|
managedClasses,
|
|
fileData,
|
|
}: FileViewProps) => {
|
|
const { id, extension, fromServer } = fileData;
|
|
|
|
/**
|
|
* Helper functions as refs to prevent them from updating after
|
|
* each re-render
|
|
*/
|
|
const onImageLoaded = useRef<() => void>(null);
|
|
const onMagnify = useRef<React.MouseEventHandler>(() => {});
|
|
|
|
/**
|
|
* Used to decide whether to view the thumbnail or the original image
|
|
*/
|
|
const [largeImageLoaded, setLargeImageLoadedState] = useState(false);
|
|
|
|
/**
|
|
* Image manipulation involves overflowing the body
|
|
* Moreover, <DashboardList/> uses a body scrollbar which we want to hide.
|
|
*/
|
|
useEffect(() => {
|
|
document.body.style.overflow = "hidden";
|
|
return () => {
|
|
document.body.style.overflow = "visible";
|
|
};
|
|
}, []);
|
|
|
|
/**
|
|
* Setting up imageLoad event handler for large image
|
|
*/
|
|
useEffect(() => {
|
|
onImageLoaded.current = () => setLargeImageLoadedState(true);
|
|
return () => {
|
|
if (largeImage) largeImage.removeEventListener("load", onImageLoaded.current);
|
|
};
|
|
}, []);
|
|
|
|
/**
|
|
* Load image in background before rendering
|
|
*/
|
|
const loadLargeImage = () => {
|
|
largeImage = new Image();
|
|
largeImage.addEventListener("load", onImageLoaded.current);
|
|
largeImage.src = `${window.location.origin}/${id}.${extension}`;
|
|
};
|
|
|
|
// imageURL for <ImageViewer/>
|
|
const imageURL = `${window.location.origin}/${id}.${
|
|
largeImageLoaded || fromServer ? extension : "thumb.jpg"
|
|
}`;
|
|
|
|
// Callback used after mounting ImageViewer
|
|
const setZoomRef = (ref: React.MouseEventHandler) => (onMagnify.current = ref);
|
|
|
|
return (
|
|
<FVSidebarProvider fileData={fileData}>
|
|
<div className={managedClasses.fileView}>
|
|
<motion.div
|
|
className={managedClasses.fileViewBackground}
|
|
initial={{ opacity: 0 }}
|
|
animate={{ opacity: 1 }}
|
|
transition={{ duration: 0.3 }}
|
|
exit={{ opacity: 0 }}
|
|
onAnimationComplete={loadLargeImage}
|
|
>
|
|
<Header
|
|
position="absolute"
|
|
jssStyleSheet={headerStyles}
|
|
centerContent={<HeaderCenterContent />}
|
|
rightSideContent={<HeaderRightContent onMagnify={onMagnify.current} />}
|
|
/>
|
|
<FVSidebarToggleButton />
|
|
</motion.div>
|
|
<ImageViewer imageURL={imageURL} fileData={fileData} zoomRef={setZoomRef} />
|
|
{largeImageLoaded && <FVSidebar fileData={fileData} />}
|
|
</div>
|
|
</FVSidebarProvider>
|
|
);
|
|
};
|
|
|
|
export default manageJss(styles)(FileView);
|