Shadis/src/FileView/views/FVSidebar/FVSidebarToggleButton.tsx

133 lines
3.8 KiB
TypeScript
Executable File

import React, { useContext, useState, useEffect } from "react";
import {
FVSidebarToggleButtonProps,
FVSidebarToggleButtonClassNameContract,
} from "./FVSidebarToggleButton.props";
import {
DesignSystem,
applyPillCornerRadius,
neutralLayerL2,
} from "@microsoft/fast-components-styles-msft";
import manageJss, { ComponentStyles } from "@microsoft/fast-jss-manager-react";
import { motion, useTransform } from "framer-motion";
import { FaCaretLeft, FaInfo } from "react-icons/fa";
import { SidebarData } from "./FVSidebarContext";
import { parseColorHexRGBA } from "@microsoft/fast-colors";
import { designSystemContext } from "@microsoft/fast-jss-manager-react/dist/context";
import { ButtonClassNameContract } from "../../../_DesignSystem/Button/Button.props";
import { Button } from "../../../_DesignSystem";
/**
* The position on the x-axis where the button is placed by default
*/
export const defaultButtonPos = 17;
/**
* Styling for the component
*/
const styles: ComponentStyles<FVSidebarToggleButtonClassNameContract, DesignSystem> = {
fv_sidebarButton: {
zIndex: "63",
position: "absolute",
right: defaultButtonPos + "px",
top: "15px",
...applyPillCornerRadius(),
},
};
/**
* Custom styling for sidebar toggle.
*/
const customCaretStyle: ComponentStyles<ButtonClassNameContract, DesignSystem> = {
button: {
paddingTop: "6px",
paddingLeft: "6px",
margin: "0",
"& > svg": {
paddingBottom: "2px",
},
"& > div": {
height: "17px",
},
},
};
const FVSidebarToggleButton: React.ComponentType<FVSidebarToggleButtonProps> = ({
managedClasses,
}) => {
const { isSidebarVisible, setSidebarVisibility, sidebarPos } = useContext(SidebarData);
const [isButtonHover, setButtonHover] = useState(false);
const designCtx = useContext(designSystemContext) as DesignSystem;
/**
* Moves the button into the sidebar when opened.
* Consists of buttonPos + buttonWidth + paddingLeft
*/
const buttonPosition = useTransform(sidebarPos, pos =>
Math.min((pos - (defaultButtonPos + 64)) * -1, 0)
);
/**
* Change background-color if sidebarPos > defaultButtonPos
* The first color needs to be the same as the second one only in transparent.
*/
const buttonBackground = useTransform(
sidebarPos,
[defaultButtonPos, 82],
[
parseColorHexRGBA(neutralLayerL2(designCtx) + "00").toStringWebRGBA(),
neutralLayerL2(designCtx),
]
);
/**
* Rotate icon if sidebar is opened.
*/
const buttonIconRotation = useTransform(sidebarPos, [defaultButtonPos, 82], [0, 180]);
/**
* Resetting hover state if `isSidebarVisible` state changes.
* This is for avoiding visual bugs.
*/
useEffect(() => setButtonHover(false), [isSidebarVisible]);
return (
<motion.div
className={managedClasses.fv_sidebarButton}
style={{
x: buttonPosition,
background: buttonBackground,
}}
onHoverStart={() => setButtonHover(true)}
onHoverEnd={() => setButtonHover(false)}
>
<Button
beforeContent={classname => (
<>
<motion.div
animate={{
/**
* Hover animation.
*
* We also move the caret by a few pixels
* when the sidebar is opened
* to ensure optical centering
*/
x: isButtonHover ? (isSidebarVisible ? 5 : -3) : isSidebarVisible ? 2 : 0,
}}
style={{ rotate: buttonIconRotation }}
>
<FaCaretLeft className={classname} />
</motion.div>
<FaInfo className={classname} />
</>
)}
onClick={() => setSidebarVisibility(!isSidebarVisible)}
jssStyleSheet={customCaretStyle}
/>
</motion.div>
);
};
export default manageJss(styles)(FVSidebarToggleButton);