1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-11-26 22:41:17 +02:00

Chore: Improve types for mobile and desktop themeStyle (#10297)

This commit is contained in:
Henry Heino
2024-04-11 00:35:20 -07:00
committed by GitHub
parent 55d25308f8
commit 346f49fa66
28 changed files with 408 additions and 378 deletions

View File

@@ -7,11 +7,11 @@ import theme_nord from './themes/nord';
import theme_aritimDark from './themes/aritimDark';
import theme_oledDark from './themes/oledDark';
import Setting from './models/Setting';
import { Theme, ThemeAppearance } from './themes/type';
const Color = require('color');
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const themes: any = {
const themes: Record<number, Theme> = {
[Setting.THEME_LIGHT]: theme_light,
[Setting.THEME_DARK]: theme_dark,
[Setting.THEME_DRACULA]: theme_dracula,
@@ -22,340 +22,330 @@ const themes: any = {
[Setting.THEME_OLED_DARK]: theme_oledDark,
};
export function themeById(themeId: string) {
export function themeById(themeId: number) {
if (!themes[themeId]) throw new Error(`Invalid theme ID: ${themeId}`);
const output = { ...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;
return { ...themes[themeId] };
}
const literal = <T extends string> (str: T): T => str;
// globalStyle should be used for properties that do not change across themes
// i.e. should not be used for colors
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
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,
};
const globalStyle = (() => {
globalStyle.marginRight = globalStyle.margin;
globalStyle.marginLeft = globalStyle.margin;
globalStyle.marginTop = globalStyle.margin;
globalStyle.marginBottom = globalStyle.margin;
const margin = 15; // No text and no interactive component should be within this margin
const fontFamily = 'Roboto'; // 'sans-serif',
return {
fontFamily: fontFamily,
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: ThemeAppearance.Light,
mainPadding: 12,
topRowHeight: 50,
editorPaddingLeft: 8,
globalStyle.icon = {
fontSize: 30,
};
margin: margin,
marginRight: margin,
marginLeft: margin,
marginTop: margin,
marginBottom: margin,
globalStyle.lineInput = {
fontFamily: globalStyle.fontFamily,
maxHeight: 22,
height: 22,
paddingLeft: 5,
};
icon: { fontSize: 30 },
lineInput: {
fontFamily,
maxHeight: 22,
height: 22,
paddingLeft: 5,
},
headerStyle: {
fontFamily,
},
inputStyle: {
border: '1px solid',
height: 24,
maxHeight: 24,
paddingLeft: 5,
paddingRight: 5,
boxSizing: literal('border-box'),
},
containerStyle: {
overflow: literal('auto'),
overflowY: literal('auto'),
},
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)',
borderRadius: 4,
},
};
})();
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,
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
export function addExtraStyles(style: any) {
export function extraStyles(theme: Theme) {
const zoomRatio = 1;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const fontSizes: any = {
fontSize: Math.round(12 * zoomRatio),
const baseFontSize = Math.round(12 * zoomRatio);
const fontSizes = {
fontSize: baseFontSize,
toolbarIconSize: 18,
noteViewerFontSize: Math.round(baseFontSize * 1.25),
};
fontSizes.noteViewerFontSize = Math.round(fontSizes.fontSize * 1.25);
const bgColor4 = theme.backgroundColor4;
const borderColor4: string = Color(theme.color).alpha(0.3);
const iconColor = Color(theme.color).alpha(0.8);
style.zoomRatio = zoomRatio;
const backgroundColor5 = theme.backgroundColor5 ?? theme.color4;
const backgroundColorHover5 = Color(backgroundColor5).darken(0.2).hex();
const backgroundColorActive5 = Color(backgroundColor5).darken(0.4).hex();
style = { ...fontSizes, ...style };
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.noteListHeaderHeight = 26;
style.noteListHeaderBorderPadding = 4;
style.icon = {
...style.icon,
color: style.color,
const inputStyle = {
...globalStyle.inputStyle,
color: theme.color,
backgroundColor: theme.backgroundColor,
borderColor: theme.dividerColor,
};
style.lineInput = {
...style.lineInput,
color: style.color,
backgroundColor: style.backgroundColor,
const containerStyle = {
...globalStyle.containerStyle,
color: theme.color,
backgroundColor: theme.backgroundColor,
};
style.headerStyle = {
...style.headerStyle,
color: style.color,
backgroundColor: style.backgroundColor,
};
style.inputStyle = {
...style.inputStyle,
color: style.color,
backgroundColor: style.backgroundColor,
borderColor: style.dividerColor,
};
style.containerStyle = {
...style.containerStyle,
color: style.color,
backgroundColor: style.backgroundColor,
};
style.buttonStyle = {
...style.buttonStyle,
color: style.color4,
backgroundColor: style.backgroundColor4,
borderColor: style.borderColor4,
userSelect: 'none',
const buttonStyle = {
...globalStyle.buttonStyle,
color: theme.color4,
backgroundColor: theme.backgroundColor4,
borderColor: borderColor4,
userSelect: literal('none'),
// cursor: 'pointer',
};
style.tagStyle = {
fontSize: style.fontSize,
fontFamily: style.fontFamily,
const tagStyle = {
fontSize: baseFontSize,
fontFamily: globalStyle.fontFamily,
paddingTop: 4,
paddingBottom: 4,
paddingRight: 10,
paddingLeft: 10,
backgroundColor: style.backgroundColor3,
color: style.color3,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: theme.backgroundColor3,
color: theme.color3,
display: literal('flex'),
alignItems: literal('center'),
justifyContent: literal('center'),
marginRight: 8,
borderRadius: 100,
borderWidth: 0,
};
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 = {
const toolbarStyle = {
height: globalStyle.toolbarHeight,
minWidth: globalStyle.toolbarHeight,
display: literal('flex'),
alignItems: literal('center'),
paddingLeft: globalStyle.headerButtonHPadding,
paddingRight: globalStyle.headerButtonHPadding,
textDecoration: literal('none'),
fontFamily: globalStyle.fontFamily,
fontSize: style.fontSize,
fontSize: baseFontSize,
boxSizing: literal('border-box'),
cursor: literal('default'),
justifyContent: literal('center'),
color: theme.color,
whiteSpace: literal('nowrap'),
};
const textStyle = {
fontFamily: globalStyle.fontFamily,
fontSize: baseFontSize,
lineHeight: '1.6em',
color: style.color,
color: theme.color,
};
style.clickableTextStyle = { ...style.textStyle, userSelect: 'none' };
style.textStyle2 = { ...style.textStyle,
color: style.color2,
const textStyle2 = {
...textStyle,
color: theme.color2,
};
style.textStyleMinor = { ...style.textStyle,
color: style.colorFaded,
fontSize: style.fontSize * 0.8,
const textStyleMinor = { ...textStyle,
color: theme.colorFaded,
fontSize: baseFontSize * 0.8,
};
style.urlStyle = { ...style.textStyle,
textDecoration: 'underline',
color: style.urlColor,
const urlStyle = {
...textStyle,
textDecoration: literal('underline'),
color: theme.urlColor,
};
style.h1Style = {
...style.textStyle,
color: style.color,
fontSize: style.textStyle.fontSize * 1.5,
fontWeight: 'bold',
const h1Style = {
...textStyle,
color: theme.color,
fontSize: textStyle.fontSize * 1.5,
fontWeight: literal('bold'),
};
style.h2Style = {
...style.textStyle,
color: style.color,
fontSize: style.textStyle.fontSize * 1.3,
fontWeight: 'bold',
const h2Style = {
...textStyle,
color: theme.color,
fontSize: textStyle.fontSize * 1.3,
fontWeight: literal('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',
return {
zoomRatio,
...fontSizes,
selectedDividerColor: Color(theme.dividerColor).darken(0.2).hex(),
iconColor,
colorFaded2: Color(theme.color2).alpha(0.5).rgb(),
colorHover2: Color(theme.color2).alpha(0.7).rgb(),
colorActive2: Color(theme.color2).alpha(0.9).rgb(),
backgroundColorHoverDim3: Color(theme.backgroundColorHover3).alpha(0.3).rgb(),
backgroundColorActive3: Color(theme.backgroundColorHover3).alpha(0.5).rgb(),
backgroundColorHover2: Color(theme.selectedColor2).alpha(0.4).rgb(),
backgroundColorHover4: Color(theme.backgroundColorHover3).alpha(0.3).rgb(),
backgroundColorActive4: Color(theme.backgroundColorHover3).alpha(0.8).rgb(),
colorHover3: theme.color3,
borderColor4,
backgroundColor4: bgColor4,
color5: theme.color5 ?? bgColor4,
backgroundColor5,
backgroundColorHover5,
backgroundColorActive5,
icon: {
...globalStyle.icon,
color: theme.color,
},
lineInput: {
...globalStyle.lineInput,
color: theme.color,
backgroundColor: theme.backgroundColor,
},
containerStyle,
configScreenPadding: globalStyle.mainPadding * 2,
noteListHeaderHeight: 26,
noteListHeaderBorderPadding: 4,
textStyle,
textStyle2,
textStyleMinor,
clickableTextStyle: { ...textStyle, userSelect: literal('none') },
headerStyle: {
...globalStyle.headerStyle,
color: theme.color,
backgroundColor: theme.backgroundColor,
},
h1Style,
h2Style,
urlStyle,
inputStyle,
toolbarStyle,
tagStyle,
buttonStyle,
dialogModalLayer: {
zIndex: 9999,
display: literal('flex'),
position: literal('absolute'),
top: 0,
left: 0,
width: '100%',
height: '100%',
backgroundColor: 'rgba(0,0,0,0.6)',
alignItems: literal('flex-start'),
justifyContent: literal('center'),
},
controlBox: {
marginBottom: '1em',
color: 'black', // This will apply for the calendar
display: literal('flex'),
flexDirection: literal('row'),
alignItems: literal('center'),
},
controlBoxLabel: {
marginRight: '1em',
width: '10em',
display: literal('inline-block'),
fontWeight: literal('bold'),
},
controlBoxValue: {
display: literal('inline-block'),
},
dialogBox: {
backgroundColor: theme.backgroundColor,
padding: 16,
boxShadow: '6px 6px 20px rgba(0,0,0,0.5)',
marginTop: 20,
maxHeight: '80%',
display: literal('flex'),
flexDirection: literal('column'),
},
buttonIconStyle: {
color: iconColor,
marginRight: 6,
},
notificationBox: {
backgroundColor: theme.warningBackgroundColor,
display: literal('flex'),
alignItems: literal('center'),
padding: 10,
fontSize: baseFontSize,
},
dialogTitle: { ...h1Style, marginBottom: '1.2em' },
dropdownList: { ...inputStyle },
colorHover: theme.color,
backgroundHover: `${theme.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.
highlightedColor: theme.highlightedColor ?? theme.selectedColor2,
};
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 = { ...style.h1Style, marginBottom: '1.2em' };
style.dropdownList = { ...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;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const themeCache_: any = {};
type ExtraStyles = ReturnType<typeof extraStyles>;
type GlobalStyle = typeof globalStyle;
export type ThemeStyle = Theme & ExtraStyles & GlobalStyle & { cacheKey: number };
export function themeStyle(themeId: number) {
const themeCache_: Record<string, ThemeStyle> = {};
export function themeStyle(themeId: number): ThemeStyle {
if (!themeId) throw new Error('Theme must be specified');
const cacheKey = themeId;
if (themeCache_[cacheKey]) return themeCache_[cacheKey];
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
let output: any = {};
const output: ThemeStyle = {
cacheKey,
...globalStyle,
...themes[themeId],
// All theme are based on the light style, and just override the
// relevant properties
output = { ...globalStyle, ...themes[themeId] };
output = addExtraStyles(output);
output.cacheKey = cacheKey;
// All theme are based on the light style, and just override the
// relevant properties
...extraStyles(themes[themeId]),
};
themeCache_[cacheKey] = output;
return themeCache_[cacheKey];
@@ -370,8 +360,10 @@ const cachedStyles_: any = {
// 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.
// eslint-disable-next-line @typescript-eslint/ban-types, @typescript-eslint/no-explicit-any -- Old code before rule was applied, Old code before rule was applied
export function buildStyle(cacheKey: any, themeId: number, callback: Function) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Partially refactored code from before rule was applied
type BuildStyleCallback = (style: ThemeStyle)=> any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code from before rule was applied
export function buildStyle(cacheKey: any, themeId: number, callback: BuildStyleCallback) {
cacheKey = Array.isArray(cacheKey) ? cacheKey.join('_') : cacheKey;
// We clear the cache whenever switching themes