ToolJet/frontend/src/_components/DarkModeToggle.jsx

120 lines
3.7 KiB
JavaScript

import React from 'react';
import { useSpring, animated } from 'react-spring';
import OverlayTrigger from 'react-bootstrap/OverlayTrigger';
import Tooltip from 'react-bootstrap/Tooltip';
import { useTranslation } from 'react-i18next';
import classnames from 'classnames';
import useStore from '@/AppBuilder/_stores/store';
import { shallow } from 'zustand/shallow';
export const DarkModeToggle = function DarkModeToggle({
darkMode = false,
switchDarkMode,
tooltipPlacement = 'bottom',
showText = false,
}) {
const setResolvedGlobals = useStore((state) => state.setResolvedGlobals, shallow);
const appMode = useStore((state) => state.globalSettings.appMode, shallow);
const toggleDarkMode = () => {
switchDarkMode(!darkMode);
if (appMode === 'auto') {
setResolvedGlobals('theme', { name: !darkMode ? 'dark' : 'light' });
}
};
const { t } = useTranslation();
const properties = {
sun: {
r: 9,
transform: 'rotate(40deg)',
cx: 12,
cy: 4,
opacity: 0,
},
moon: {
r: 5,
transform: 'rotate(90deg)',
cx: 30,
cy: 0,
opacity: 1,
},
springConfig: { mass: 4, tension: 250, friction: 35 },
};
const { r, transform, cx, cy, opacity } = properties[darkMode ? 'moon' : 'sun'];
const svgContainerProps = useSpring({
transform,
config: properties.springConfig,
});
const centerCircleProps = useSpring({ r, config: properties.springConfig });
const maskedCircleProps = useSpring({
cx,
cy,
color: 'white',
config: properties.springConfig,
});
const linesProps = useSpring({ opacity, config: properties.springConfig });
return (
<OverlayTrigger
placement={tooltipPlacement}
delay={{ show: 250, hide: 400 }}
trigger={('hover', 'focus')}
overlay={
<Tooltip id="button-tooltip">
{darkMode
? t('header.darkModeToggle.activateLightMode', 'Activate light mode')
: t('header.darkModeToggle.activateDarkMode', 'Activate dark mode')}
</Tooltip>
}
>
<div
className={classnames('unstyled-button dark-theme-toggle-btn sidebar-svg-icon left-sidebar-item')}
onClick={toggleDarkMode}
>
<animated.svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
color={'var(--slate8)'}
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
stroke="currentColor"
style={{
cursor: 'pointer',
...svgContainerProps,
transform: 'rotate(315deg)',
}}
>
<mask id="myMask2">
<rect x="0" y="0" width="100%" height="100%" fill="white" />
<animated.circle style={maskedCircleProps} r="9" fill="black" />
</mask>
<animated.circle cx="12" cy="12" style={centerCircleProps} fill={'var(--slate8)'} mask="url(#myMask2)" />
<animated.g stroke="currentColor" style={linesProps}>
<line x1="12" y1="1" x2="12" y2="3" />
<line x1="12" y1="21" x2="12" y2="23" />
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64" />
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78" />
<line x1="1" y1="12" x2="3" y2="12" />
<line x1="21" y1="12" x2="23" y2="12" />
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36" />
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22" />
</animated.g>
</animated.svg>
{showText && (
<span className="dark-theme-toggle-btn-text" onClick={toggleDarkMode}>
Switch to {!darkMode ? 'dark mode' : 'light mode'}
</span>
)}
</div>
</OverlayTrigger>
);
};