Advertisement
Guest User

Untitled

a guest
Apr 6th, 2020
384
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. import React from 'react';
  2. import styled from 'styled-components';
  3. import { useSpring, useTrail, animated } from 'react-spring';
  4.  
  5. import { BREAKPOINTS } from '@constants';
  6. import useSound from '@hooks/use-sound.hook';
  7. import { generateId } from '@utils';
  8.  
  9. import UnstyledButton from '../UnstyledButton';
  10. import { ConfigContext } from '../ConfigContext';
  11.  
  12. export const DarkModeToggle = ({
  13.   colorMode,
  14.   setColorMode,
  15.   size = 18,
  16.   ...delegated
  17. }) => {
  18.   const { current: id } = React.useRef(generateId());
  19.  
  20.   const [playOn] = useSound('/sounds/switch-on.mp3');
  21.   const [playOff] = useSound('/sounds/switch-off.mp3');
  22.  
  23.   const isDark = colorMode === 'dark';
  24.  
  25.   function toggleColorMode(event) {
  26.     event.preventDefault();
  27.     setColorMode(isDark ? 'light' : 'dark');
  28.  
  29.     if (isDark) {
  30.       playOn();
  31.     } else {
  32.       playOff();
  33.     }
  34.   }
  35.  
  36.   const svgSpring = useSpring({
  37.     transform: isDark ? 'rotate(40deg)' : 'rotate(90deg)',
  38.   });
  39.   const maskSpring = useSpring({
  40.     cx: isDark ? 10 : 25,
  41.     cy: isDark ? 2 : 0,
  42.     config: {
  43.       mass: 3.1,
  44.       friction: 21,
  45.     },
  46.   });
  47.   const sunMoonSpring = useSpring({
  48.     r: isDark ? 8 : 5,
  49.   });
  50.  
  51.   const sunDotAngles = [0, 60, 120, 180, 240, 300];
  52.  
  53.   const sunDotTrail = useTrail(sunDotAngles.length, {
  54.     transform: isDark ? 0 : 1,
  55.     transformOrigin: 'center center',
  56.     immediate: isDark,
  57.     config: {
  58.       tension: 210,
  59.       friction: 20,
  60.     },
  61.   });
  62.  
  63.   return (
  64.     <IconWrapper
  65.       onClick={toggleColorMode}
  66.       aria-label={isDark ? 'Activate light mode' : 'Activate dark mode'}
  67.       title={isDark ? 'Activate light mode' : 'Activate dark mode'}
  68.       {...delegated}
  69.     >
  70.       <MoonOrSun
  71.         width={size}
  72.         height={size}
  73.         viewBox="0 0 18 18"
  74.         style={svgSpring}
  75.       >
  76.         <mask id={`moon-mask-${id}`}>
  77.           <rect x="0" y="0" width="18" height="18" fill="#FFF" />
  78.           <animated.circle {...maskSpring} r="8" fill="black" />
  79.         </mask>
  80.  
  81.         <animated.circle
  82.           cx="9"
  83.           cy="9"
  84.           fill="var(--color-text)"
  85.           mask={`url(#moon-mask-${id})`}
  86.           {...sunMoonSpring}
  87.         />
  88.  
  89.         {/* Sun dots */}
  90.         <g>
  91.           {sunDotTrail.map(({ transform, ...props }, index) => {
  92.             const angle = sunDotAngles[index];
  93.             const centerX = 9;
  94.             const centerY = 9;
  95.  
  96.             const angleInRads = (angle / 180) * Math.PI;
  97.  
  98.             const c = 8; // hypothenuse
  99.             const a = centerX + c * Math.cos(angleInRads);
  100.             const b = centerY + c * Math.sin(angleInRads);
  101.  
  102.             return (
  103.               <animated.circle
  104.                 key={angle}
  105.                 cx={a}
  106.                 cy={b}
  107.                 r={1.5}
  108.                 fill="var(--color-text)"
  109.                 style={{
  110.                   ...props,
  111.                   transform: transform.interpolate(t => `scale(${t})`),
  112.                 }}
  113.               />
  114.             );
  115.           })}
  116.         </g>
  117.       </MoonOrSun>
  118.     </IconWrapper>
  119.   );
  120. };
  121.  
  122. const IconWrapper = styled(UnstyledButton)`
  123.   opacity: 0.7;
  124.   position: relative;
  125.   border-radius: 5px;
  126.   width: 40px;
  127.   height: 32px;
  128.   display: flex;
  129.   align-items: center;
  130.   justify-content: center;
  131.  
  132.   @media ${BREAKPOINTS.mdMin} {
  133.     &:hover {
  134.       opacity: 1;
  135.     }
  136.   }
  137. `;
  138.  
  139. const MoonOrSun = styled(animated.svg)`
  140.   position: relative;
  141.   overflow: visible;
  142. `;
  143.  
  144. const DarkModeToggleContainer = delegated => {
  145.   const { colorMode, setColorMode } = React.useContext(ConfigContext);
  146.  
  147.   return (
  148.     <DarkModeToggle
  149.       colorMode={colorMode}
  150.       setColorMode={setColorMode}
  151.       {...delegated}
  152.     />
  153.   );
  154. };
  155.  
  156. export default DarkModeToggleContainer;
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement