import React, { FC } from 'react';
import styled from 'styled-components';
import { ColorScheme } from '../../../plugins';
import { PluginTheme } from '../../../plugins/misc';
import { useThemes } from '../../hooks/theme';
import { HelpTooltip } from '../help-tooltip';
const THEMES_PER_ROW = 5;
const isDark = (mode: 'dark' | 'light') => mode === 'dark';
const isLight = (mode: 'dark' | 'light') => mode === 'light';
const RootWrapper = styled.div({});
const CheckboxWrapper = styled.div({
marginLeft: 'var(--padding-md)',
});
const Themes = styled.div({
display: 'flex',
flexWrap: 'wrap',
});
const ThemeButton = styled.div<{ $isActive: boolean; $isInOsThemeMode: boolean }>(({ $isActive, $isInOsThemeMode }) => ({
position: 'relative',
margin: 'var(--padding-md) var(--padding-md)',
fontSize: 0,
borderRadius: 'var(--radius-md)',
transition: 'all 150ms ease-out',
// This is a workaround for some anti-aliasing artifacts that impact the color scheme badges.
// The box shadow is placed on a pseudo-element. When it is active, it is configured to overlap
// 1px with the underlying geometry to prevent gaps caused by anti-aliasing.
'&:before': {
display: 'block',
position: 'absolute',
boxShadow: '0 0 0 1px var(--hl-sm)',
transition: 'all 150ms ease-out',
borderRadius: 'var(--radius-md)',
width: '100%',
height: '100%',
content: "''",
...($isActive ? {
boxShadow: '0 0 0 var(--padding-xs) var(--color-surprise)',
width: 'calc(100% - 2px)',
height: 'calc(100% - 2px)',
margin: '1px',
} : {}),
},
'&:hover:before': {
...($isInOsThemeMode ? { boxShadow: '0 0 0 0 var(--color-surprise)' } : {}),
},
...($isActive ? {
transform: 'scale(1.05)',
} : {}),
'&:hover': {
transform: 'scale(1.05)',
},
...($isInOsThemeMode ? {
'&:hover .overlay-wrapper': {
display: 'flex',
},
'&:hover .theme-preview': {
visibility: 'hidden', // this prevents alpha-blending problems with the underlying svg bleeding through
},
} : {}),
}));
const ThemeTitle = styled.h2({
marginTop: 0,
marginBottom: 'var(--padding-xs)',
fontSize: 'var(--font-size-md)',
});
const ThemeWrapper = styled.div({
maxWidth: `${100 / THEMES_PER_ROW}%`,
minWidth: 110,
marginBottom: 'var(--padding-md)',
marginTop: 'var(--padding-md)',
textAlign: 'center',
});
const ColorSchemeBadge = styled.div<{ $theme: 'dark' | 'light'}>(({ $theme }) => ({
position: 'absolute',
top: 0,
width: 12,
height: 12,
fill: 'white',
padding: 4,
transition: 'background-color 150ms ease-out',
backgroundColor: 'var(--color-surprise)',
...(isDark($theme) ? {
right: 0,
borderTopRightRadius: 'var(--radius-md)',
borderBottomLeftRadius: 'var(--radius-md)',
} : {}),
...(isLight($theme) ? {
left: 0,
borderTopLeftRadius: 'var(--radius-md)',
borderBottomRightRadius: 'var(--radius-md)',
} : {}),
}));
const OverlayWrapper = styled.div({
position: 'absolute',
height: '100%',
width: '100%',
alignItems: 'center',
flexWrap: 'nowrap',
justifyContent: 'space-evenly',
boxSizing: 'content-box',
display: 'none', // controlled by hover on the ThemeWrapper
});
const OverlaySide = styled.div<{ $theme: 'dark' | 'light' }>(({ $theme }) => ({
display: 'flex',
cursor: 'pointer',
flexGrow: 1,
alignItems: 'center',
justifyContent: 'center',
height: '100%',
background: 'var(--color-bg)',
'& svg': {
opacity: 0.4,
fill: 'var(--color-fg)',
height: 20,
},
'&:hover svg': {
opacity: 1,
fill: 'var(--color-surprise)',
},
boxSizing: 'border-box',
border: '1px solid var(--hl-sm)',
...(isDark($theme) ? {
borderTopRightRadius: 'var(--radius-md)',
borderBottomRightRadius: 'var(--radius-md)',
borderLeftStyle: 'none',
} : {}),
...(isLight($theme) ? {
borderTopLeftRadius: 'var(--radius-md)',
borderBottomLeftRadius: 'var(--radius-md)',
borderRightStyle: 'none',
} : {}),
'&:hover': {
border: '1px solid var(--color-surprise)',
},
}));
const SunSvg = () => (
);
const MoonSvg = () => (
);
const ThemePreview: FC<{ theme: PluginTheme }> = ({ theme: { name: themeName } }) => (
);
const IndividualTheme: FC<{
isActive: boolean;
isDark: boolean;
isLight: boolean;
isInOsThemeMode: boolean;
onChangeTheme: (name: string, mode: ColorScheme) => Promise;
theme: PluginTheme;
}> = ({
isActive,
isDark,
isLight,
isInOsThemeMode,
onChangeTheme,
theme,
}) => {
const { displayName, name } = theme;
const onClickThemeButton = () => {
if (isInOsThemeMode) {
// The overlays handle this behavior in OS theme mode.
// React's event bubbling means that this will be fired when you click on an overlay, so we need to turn it off when in this mode.
// Even still, we don't want to risk some potnetial subpixel or z-index nonsense accidentally setting the default when know we shouldn't.
return;
}
return onChangeTheme(name, 'default');
};
return (
{displayName}
{isInOsThemeMode ? (
<>
{ onChangeTheme(name, 'light'); }}>
{ onChangeTheme(name, 'dark'); }}>
{isActive && isDark ? (
) : null}
{isActive && isLight ? (
) : null}
>
) : null}
);
};
export const ThemePanel: FC = () => {
const {
themes,
activate,
changeAutoDetect,
isActive,
isActiveDark,
isActiveLight,
autoDetectColorScheme,
} = useThemes();
return (
{themes.map(theme => (
))}
);
};