1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-03 08:35:29 +02:00
joplin/packages/lib/theme.ts
Laurent Cozic b2b6ad479a Revert "Desktop: Make font size consistent between Markdown and Rich Text editors"
This reverts commit a058e09183.

Reverts because this change means the settings are directly accessed
from the theme, which makes the themes unusable from Joplin Server.
2021-06-10 10:59:15 +02:00

430 lines
11 KiB
TypeScript

import { Theme } from './themes/type';
import theme_light from './themes/light';
import theme_dark from './themes/dark';
import theme_dracula from './themes/dracula';
import theme_solarizedLight from './themes/solarizedLight';
import theme_solarizedDark from './themes/solarizedDark';
import theme_nord from './themes/nord';
import theme_aritimDark from './themes/aritimDark';
import theme_oledDark from './themes/oledDark';
import Setting from './models/Setting';
const Color = require('color');
const themes: any = {
[Setting.THEME_LIGHT]: theme_light,
[Setting.THEME_DARK]: theme_dark,
[Setting.THEME_DRACULA]: theme_dracula,
[Setting.THEME_SOLARIZED_LIGHT]: theme_solarizedLight,
[Setting.THEME_SOLARIZED_DARK]: theme_solarizedDark,
[Setting.THEME_NORD]: theme_nord,
[Setting.THEME_ARITIM_DARK]: theme_aritimDark,
[Setting.THEME_OLED_DARK]: theme_oledDark,
};
function themeById(themeId: string) {
if (!themes[themeId]) throw new Error(`Invalid theme ID: ${themeId}`);
const output = Object.assign({}, themes[themeId]);
if (!output.headerBackgroundColor) {
output.headerBackgroundColor = output.appearance === 'light' ? '#F0F0F0' : '#2D3136';
}
if (!output.textSelectionColor) {
output.textSelectionColor = output.appearance === 'light' ? '#0096FF' : '#00AEFF';
}
if (!output.colorBright2) {
output.colorBright2 = output.appearance === 'light' ? '#ffffff' : '#ffffff';
}
return output;
}
// globalStyle should be used for properties that do not change across themes
// i.e. should not be used for colors
const globalStyle: any = {
fontFamily: 'Roboto',// 'sans-serif',
margin: 15, // No text and no interactive component should be within this margin
itemMarginTop: 10,
itemMarginBottom: 10,
disabledOpacity: 0.3,
buttonMinWidth: 50,
buttonMinHeight: 30,
editorFontSize: 12,
textAreaLineHeight: 17,
lineHeight: '1.6em',
headerButtonHPadding: 6,
toolbarHeight: 26,
toolbarPadding: 6,
appearance: 'light',
mainPadding: 12,
topRowHeight: 50,
editorPaddingLeft: 8,
};
globalStyle.marginRight = globalStyle.margin;
globalStyle.marginLeft = globalStyle.margin;
globalStyle.marginTop = globalStyle.margin;
globalStyle.marginBottom = globalStyle.margin;
globalStyle.icon = {
fontSize: 30,
};
globalStyle.lineInput = {
fontFamily: globalStyle.fontFamily,
maxHeight: 22,
height: 22,
paddingLeft: 5,
};
globalStyle.headerStyle = {
fontFamily: globalStyle.fontFamily,
};
globalStyle.inputStyle = {
border: '1px solid',
height: 24,
maxHeight: 24,
paddingLeft: 5,
paddingRight: 5,
boxSizing: 'border-box',
};
globalStyle.containerStyle = {
overflow: 'auto',
overflowY: 'auto',
};
globalStyle.buttonStyle = {
// marginRight: 10,
border: '1px solid',
minHeight: 26,
minWidth: 80,
// maxWidth: 220,
paddingLeft: 12,
paddingRight: 12,
paddingTop: 6,
paddingBottom: 6,
// boxShadow: '0px 1px 1px rgba(0,0,0,0.3)',
fontSize: globalStyle.fontSize,
borderRadius: 4,
};
function addMissingProperties(theme: Theme) {
// if (!('backgroundColor3' in theme)) theme.backgroundColor3 = theme.backgroundColor;
// if (!('color3' in theme)) theme.color3 = theme.color;
// if (!('selectionBackgroundColor3' in theme)) {
// if (theme.appearance === 'dark') {
// theme.selectionBackgroundColor3 = '#ffffff77';
// } else {
// theme.selectionBackgroundColor3 = '#00000077';
// }
// }
// if (!('backgroundColorHover3' in theme)) theme.backgroundColorHover3 = Color(theme.selectionBackgroundColor3).alpha(0.5).rgb();
// if (!('selectionBorderColor3' in theme)) theme.selectionBorderColor3 = theme.backgroundColor3;
// TODO: pick base theme based on appearence
// const lightTheme = themes[Setting.THEME_LIGHT];
// for (const n in lightTheme) {
// if (!(n in theme)) theme[n] = lightTheme[n];
// }
return theme;
}
function addExtraStyles(style: any) {
style.selectedDividerColor = Color(style.dividerColor).darken(0.2).hex();
style.iconColor = Color(style.color).alpha(0.8);
style.colorFaded2 = Color(style.color2).alpha(0.5).rgb();
style.colorHover2 = Color(style.color2).alpha(0.7).rgb();
style.colorActive2 = Color(style.color2).alpha(0.9).rgb();
style.backgroundColorHoverDim3 = Color(style.backgroundColorHover3).alpha(0.3).rgb();
style.backgroundColorActive3 = Color(style.backgroundColorHover3).alpha(0.5).rgb();
const bgColor4 = style.backgroundColor4;
style.backgroundColorHover2 = Color(style.selectedColor2).alpha(0.4).rgb();
style.backgroundColorHover4 = Color(style.backgroundColorHover3).alpha(0.3).rgb();
style.backgroundColorActive4 = Color(style.backgroundColorHover3).alpha(0.8).rgb();
style.borderColor4 = Color(style.color).alpha(0.3);
style.backgroundColor4 = bgColor4;
style.color5 = bgColor4;
style.backgroundColor5 = style.color4;
style.backgroundColorHover5 = Color(style.backgroundColor5).darken(0.2).hex();
style.backgroundColorActive5 = Color(style.backgroundColor5).darken(0.4).hex();
style.configScreenPadding = style.mainPadding * 2;
style.icon = Object.assign({},
style.icon,
{ color: style.color }
);
style.lineInput = Object.assign({},
style.lineInput,
{
color: style.color,
backgroundColor: style.backgroundColor,
}
);
style.headerStyle = Object.assign({},
style.headerStyle,
{
color: style.color,
backgroundColor: style.backgroundColor,
}
);
style.inputStyle = Object.assign({},
style.inputStyle,
{
color: style.color,
backgroundColor: style.backgroundColor,
borderColor: style.dividerColor,
}
);
style.containerStyle = Object.assign({},
style.containerStyle,
{
color: style.color,
backgroundColor: style.backgroundColor,
}
);
style.buttonStyle = Object.assign({},
style.buttonStyle,
{
color: style.color4,
backgroundColor: style.backgroundColor4,
borderColor: style.borderColor4,
userSelect: 'none',
cursor: 'pointer',
}
);
style.tagStyle = {
fontSize: style.fontSize,
fontFamily: style.fontFamily,
paddingTop: 4,
paddingBottom: 4,
paddingRight: 10,
paddingLeft: 10,
backgroundColor: style.backgroundColor3,
color: style.color3,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
marginRight: 8,
borderRadius: 100,
};
style.toolbarStyle = {
height: style.toolbarHeight,
minWidth: style.toolbarHeight,
display: 'flex',
alignItems: 'center',
paddingLeft: style.headerButtonHPadding,
paddingRight: style.headerButtonHPadding,
textDecoration: 'none',
fontFamily: style.fontFamily,
fontSize: style.fontSize,
boxSizing: 'border-box',
cursor: 'default',
justifyContent: 'center',
color: style.color,
whiteSpace: 'nowrap',
};
style.textStyle = {
fontFamily: globalStyle.fontFamily,
fontSize: style.fontSize,
lineHeight: '1.6em',
color: style.color,
};
style.clickableTextStyle = Object.assign({}, style.textStyle, {
userSelect: 'none',
});
style.textStyle2 = Object.assign({}, style.textStyle,
{ color: style.color2 }
);
style.textStyleMinor = Object.assign({}, style.textStyle,
{
color: style.colorFaded,
fontSize: style.fontSize * 0.8,
}
);
style.urlStyle = Object.assign({}, style.textStyle,
{
textDecoration: 'underline',
color: style.urlColor,
}
);
style.h1Style = Object.assign({},
style.textStyle,
{
color: style.color,
fontSize: style.textStyle.fontSize * 1.5,
fontWeight: 'bold',
}
);
style.h2Style = Object.assign({},
style.textStyle,
{
color: style.color,
fontSize: style.textStyle.fontSize * 1.3,
fontWeight: 'bold',
}
);
style.dialogModalLayer = {
zIndex: 9999,
display: 'flex',
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
backgroundColor: 'rgba(0,0,0,0.6)',
alignItems: 'flex-start',
justifyContent: 'center',
};
style.controlBox = {
marginBottom: '1em',
color: 'black', // This will apply for the calendar
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
};
style.controlBoxLabel = {
marginRight: '1em',
width: '10em',
display: 'inline-block',
fontWeight: 'bold',
};
style.controlBoxValue = {
display: 'inline-block',
};
style.dialogBox = {
backgroundColor: style.backgroundColor,
padding: 16,
boxShadow: '6px 6px 20px rgba(0,0,0,0.5)',
marginTop: 20,
maxHeight: '80%',
display: 'flex',
flexDirection: 'column',
};
style.buttonIconStyle = {
color: style.iconColor,
marginRight: 6,
};
style.notificationBox = {
backgroundColor: style.warningBackgroundColor,
display: 'flex',
alignItems: 'center',
padding: 10,
fontSize: style.fontSize,
};
style.dialogTitle = Object.assign({}, style.h1Style, { marginBottom: '1.2em' });
style.dropdownList = Object.assign({}, style.inputStyle);
style.colorHover = style.color;
style.backgroundHover = `${style.selectedColor2}44`;
// In general the highlighted color, used to highlight text or icons, should be the same as selectedColor2
// but some times, depending on the theme, it might be too dark or too light, so it can be
// specified directly by the theme too.
if (!style.highlightedColor) style.highlightedColor = style.selectedColor2;
return style;
}
const themeCache_: any = {};
function themeStyle(themeId: number) {
if (!themeId) throw new Error('Theme must be specified');
const zoomRatio = 1;
const cacheKey = themeId;
if (themeCache_[cacheKey]) return themeCache_[cacheKey];
// Font size are not theme specific, but they must be referenced
// and computed here to allow them to respond to settings changes
// without the need to restart
const fontSizes: any = {
fontSize: Math.round(12 * zoomRatio),
toolbarIconSize: 18,
};
fontSizes.noteViewerFontSize = Math.round(fontSizes.fontSize * 1.25);
let output: any = {};
output.zoomRatio = zoomRatio;
// All theme are based on the light style, and just override the
// relevant properties
output = Object.assign({}, globalStyle, fontSizes, themes[themeId]);
output = addMissingProperties(output);
output = addExtraStyles(output);
output.cacheKey = cacheKey;
themeCache_[cacheKey] = output;
return themeCache_[cacheKey];
}
const cachedStyles_: any = {
themeId: null,
styles: {},
};
// cacheKey must be a globally unique key, and must change whenever
// the dependencies of the style change. If the style depends only
// on the theme, a static string can be provided as a cache key.
function buildStyle(cacheKey: any, themeId: number, callback: Function) {
cacheKey = Array.isArray(cacheKey) ? cacheKey.join('_') : cacheKey;
// We clear the cache whenever switching themes
if (cachedStyles_.themeId !== themeId) {
cachedStyles_.themeId = themeId;
cachedStyles_.styles = {};
}
if (cachedStyles_.styles[cacheKey]) return cachedStyles_.styles[cacheKey].style;
const s = callback(themeStyle(themeId));
cachedStyles_.styles[cacheKey] = {
style: s,
timestamp: Date.now(),
};
return cachedStyles_.styles[cacheKey].style;
}
export { themeStyle, buildStyle, themeById };