Shadis/src/FileView/views/ImageViewer/ImageViewerSlider.tsx

172 lines
4.5 KiB
TypeScript
Executable File

import React, { useCallback, useMemo, useContext } from "react";
import { motion } from "framer-motion";
import {
ImageViewerSliderProps,
ImageViewerSliderClassNameContract,
} from "./ImageViewerSlider.props";
import manageJss, { ComponentStyles } from "@microsoft/fast-jss-manager-react";
import {
Slider,
SliderLabel,
SliderLabelClassNameContract,
} from "@microsoft/fast-components-react-msft";
import { applyBackdropBackground, useMotionValueFactory } from "../../../_DesignSystem";
import { parseColorHexRGBA } from "@microsoft/fast-colors";
import {
SliderTrackItemAnchor,
SliderRange,
} from "@microsoft/fast-components-react-base";
import {
DesignSystem,
applyElevation,
ElevationMultiplier,
applyPillCornerRadius,
neutralLayerFloating,
} from "@microsoft/fast-components-styles-msft";
import { SidebarData } from "../FVSidebar/FVSidebarContext";
const styles: ComponentStyles<ImageViewerSliderClassNameContract, DesignSystem> = {
imageViewerSlider: {
position: "absolute",
width: "30%",
left: "0",
right: "0",
bottom: "20px",
margin: "auto",
padding: "15px 15px 20px 15px",
...applyPillCornerRadius(),
...applyBackdropBackground(
opacity => des =>
parseColorHexRGBA(neutralLayerFloating(des) + opacity).toStringWebRGBA(),
"ba"
),
...applyElevation(ElevationMultiplier.e10),
},
imageViewerSlider_label: {
display: "inline-grid",
pointerEvents: "none",
},
};
const labelStyle: ComponentStyles<SliderLabelClassNameContract, DesignSystem> = {
sliderLabel: {
cursor: "pointer",
},
};
/**
* Defines the point where the image
* is in its original size.
*/
const ogPos = 100;
/**
* A floating slider with procentual labels.
* Used in conjunction with ImageViewer.
*/
const ImageViewerSlider: React.ComponentType<ImageViewerSliderProps> = ({
managedClasses,
show,
value,
minFactor,
maxFactor,
onValueChange,
}) => {
const { sidebarPos, isSidebarFloating } = useContext(SidebarData);
/**
* Current position on the slider.
*/
type value = number;
value = value * 100;
/**
* Lowest point on the slider.
*/
const minValue = useMemo(() => minFactor * 100, [minFactor]);
/**
* Highest point on the slider.
*/
const maxValue = useMemo(() => maxFactor * 100, [maxFactor]);
/**
* Tells whether the image needed to be
* shrunk in order to fit the viewport.
*/
const isLargeImage = useMemo(() => minValue < 100, [minValue]);
/**
* Position of the fixed label that hints
* either at 1x or 2x depending on the image size.
*/
const fixedLabelPos = useMemo(() => (isLargeImage ? ogPos : maxValue - 100), [
isLargeImage,
maxValue,
]);
/**
* Decides whether showing the label would be
* intrusive or unfitting.
*/
const shouldShowLabel =
value > minValue + 5 &&
value < maxValue - 5 &&
(value < ogPos - 20 || value > ogPos + 20);
/**
* Formats the new value and passes it on to the callback prop.
* @param v THe new value of the slider
*/
const handleValueChange = useCallback(
(v: number | SliderRange) =>
(v as number).toFixed && onValueChange((v as number) / 100),
[onValueChange]
);
/**
* Moves the slider if sidebar is opened.
*/
const sliderPosX = useMotionValueFactory(
() => (isSidebarFloating ? 0 : -1 * (sidebarPos.get() / 2)),
[sidebarPos, isSidebarFloating]
);
return (
<motion.div
className={managedClasses.imageViewerSlider}
initial={{ y: "200%" }}
animate={{ y: show ? 0 : "200%" }}
style={{ x: sliderPosX }}
>
<Slider
range={{ minValue, maxValue }}
value={value}
onValueChange={handleValueChange}
>
<SliderLabel
label={isLargeImage ? "100%" : "200%"}
valuePositionBinding={fixedLabelPos}
showTickmark={true}
onClick={() => handleValueChange(fixedLabelPos)}
jssStyleSheet={labelStyle}
/>
<motion.div
className={managedClasses.imageViewerSlider_label}
initial={{ opacity: 1 }}
animate={{ opacity: shouldShowLabel ? 1 : 0 }}
transition={{ default: 0.1 }}
>
<SliderLabel
label={Math.floor((value * (isLargeImage ? 100 : 200)) / ogPos) + "%"}
valuePositionBinding={SliderTrackItemAnchor.selectedRangeMax}
showTickmark={true}
/>
</motion.div>
</Slider>
</motion.div>
);
};
export default manageJss(styles)(ImageViewerSlider);