311 lines
11 KiB
TypeScript
311 lines
11 KiB
TypeScript
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
import {CSSObject} from '@emotion/serialize'
|
|
import isEqual from 'lodash/isEqual'
|
|
import color from 'color'
|
|
|
|
import {Utils} from './utils'
|
|
|
|
let activeThemeName: string
|
|
|
|
import {UserSettings} from './userSettings'
|
|
|
|
export type Theme = {
|
|
mainBg: string,
|
|
mainFg: string,
|
|
buttonBg: string,
|
|
buttonFg: string,
|
|
sidebarBg: string,
|
|
sidebarFg: string,
|
|
sidebarTextActiveBorder: string,
|
|
sidebarWhiteLogo: string,
|
|
|
|
link: string,
|
|
linkVisited: string,
|
|
|
|
propDefault: string,
|
|
propGray: string,
|
|
propBrown: string,
|
|
propOrange: string,
|
|
propYellow: string,
|
|
propGreen: string,
|
|
propBlue: string,
|
|
propPurple: string,
|
|
propPink: string,
|
|
propRed: string,
|
|
}
|
|
|
|
export const systemThemeName = 'system-theme'
|
|
|
|
export const defaultThemeName = 'default-theme'
|
|
|
|
export const defaultTheme = {
|
|
mainBg: '255, 255, 255',
|
|
mainFg: '63, 67, 80',
|
|
buttonBg: '28, 88, 217',
|
|
buttonFg: '255, 255, 255',
|
|
sidebarBg: '30, 50, 92',
|
|
sidebarFg: '255, 255, 255',
|
|
sidebarTextActiveBorder: '93, 137, 243',
|
|
sidebarWhiteLogo: 'true',
|
|
|
|
link: '#0000ee',
|
|
linkVisited: '#551a8b',
|
|
|
|
propDefault: '#fff',
|
|
propGray: '#EDEDED',
|
|
propBrown: '#F7DDC3',
|
|
propOrange: '#ffd3c1',
|
|
propYellow: '#f7f0b6',
|
|
propGreen: '#c7eac3',
|
|
propBlue: '#B1D1F6',
|
|
propPurple: '#e6d0ff',
|
|
propPink: '#ffd6e9',
|
|
propRed: '#ffa9a9',
|
|
}
|
|
|
|
export const darkThemeName = 'dark-theme'
|
|
|
|
export const darkTheme = {
|
|
...defaultTheme,
|
|
|
|
mainBg: '55, 53, 47',
|
|
mainFg: '220, 220, 220',
|
|
buttonBg: '80, 170, 221',
|
|
buttonFg: '255, 255, 255',
|
|
sidebarBg: '75, 73, 67',
|
|
sidebarFg: '255, 255, 255',
|
|
sidebarTextActiveBorder: '102, 185, 167',
|
|
sidebarWhiteLogo: 'true',
|
|
|
|
link: '#0090ff',
|
|
linkVisited: 'hsla(270, 68%, 70%, 1.0)',
|
|
|
|
propDefault: 'hsla(0, 100%, 100%, 0.08)',
|
|
propGray: 'hsla(0, 0%, 70%, 0.4)',
|
|
propBrown: 'hsla(25, 60%, 40%, 0.4)',
|
|
propOrange: 'hsla(35, 100%, 50%, 0.4)',
|
|
propYellow: 'hsla(48, 100%, 70%, 0.4)',
|
|
propGreen: 'hsla(120, 100%, 70%, 0.4)',
|
|
propBlue: 'hsla(240, 100%, 70%, 0.4)',
|
|
propPurple: 'hsla(270, 100%, 64%, 0.4)',
|
|
propPink: 'hsla(310, 100%, 80%, 0.4)',
|
|
propRed: 'hsla(4, 100%, 70%, 0.4)',
|
|
}
|
|
|
|
export const lightThemeName = 'light-theme'
|
|
|
|
export const lightTheme = {
|
|
...defaultTheme,
|
|
|
|
mainBg: '255, 255, 255',
|
|
mainFg: '55, 53, 47',
|
|
buttonBg: '80, 170, 221',
|
|
buttonFg: '255, 255, 255',
|
|
sidebarBg: '247, 246, 243',
|
|
sidebarFg: '55, 53, 47',
|
|
sidebarTextActiveBorder: '87, 158, 255',
|
|
sidebarWhiteLogo: 'false',
|
|
}
|
|
|
|
export function setTheme(theme: Theme | null): Theme {
|
|
let consolidatedTheme = defaultTheme
|
|
if (theme) {
|
|
consolidatedTheme = {...defaultTheme, ...theme}
|
|
UserSettings.theme = JSON.stringify(consolidatedTheme)
|
|
} else {
|
|
UserSettings.theme = ''
|
|
const darkThemeMq = window.matchMedia('(prefers-color-scheme: dark)')
|
|
if (darkThemeMq.matches) {
|
|
consolidatedTheme = {...defaultTheme, ...darkTheme}
|
|
}
|
|
}
|
|
|
|
setActiveThemeName(consolidatedTheme, theme)
|
|
|
|
if (!Utils.isFocalboardPlugin()) {
|
|
document.documentElement.style.setProperty('--center-channel-bg-rgb', consolidatedTheme.mainBg)
|
|
document.documentElement.style.setProperty('--center-channel-color-rgb', consolidatedTheme.mainFg)
|
|
document.documentElement.style.setProperty('--button-bg-rgb', consolidatedTheme.buttonBg)
|
|
document.documentElement.style.setProperty('--button-color-rgb', consolidatedTheme.buttonFg)
|
|
document.documentElement.style.setProperty('--sidebar-bg-rgb', consolidatedTheme.sidebarBg)
|
|
document.documentElement.style.setProperty('--sidebar-text-rgb', consolidatedTheme.sidebarFg)
|
|
document.documentElement.style.setProperty('--link-color-rgb', consolidatedTheme.link)
|
|
document.documentElement.style.setProperty('--sidebar-text-active-border-rgb', consolidatedTheme.sidebarTextActiveBorder)
|
|
}
|
|
|
|
document.documentElement.style.setProperty('--sidebar-white-logo', consolidatedTheme.sidebarWhiteLogo)
|
|
document.documentElement.style.setProperty('--link-visited-color-rgb', consolidatedTheme.linkVisited)
|
|
|
|
const mainBgColor = color(`rgb(${getComputedStyle(document.documentElement).getPropertyValue('--center-channel-bg-rgb')})`)
|
|
|
|
if (Utils.isFocalboardPlugin()) {
|
|
let fixedTheme = lightTheme
|
|
if (mainBgColor.isDark()) {
|
|
fixedTheme = darkTheme
|
|
}
|
|
consolidatedTheme.propDefault = fixedTheme.propDefault
|
|
consolidatedTheme.propGray = fixedTheme.propGray
|
|
consolidatedTheme.propBrown = fixedTheme.propBrown
|
|
consolidatedTheme.propOrange = fixedTheme.propOrange
|
|
consolidatedTheme.propYellow = fixedTheme.propYellow
|
|
consolidatedTheme.propGreen = fixedTheme.propGreen
|
|
consolidatedTheme.propBlue = fixedTheme.propBlue
|
|
consolidatedTheme.propPurple = fixedTheme.propPurple
|
|
consolidatedTheme.propPink = fixedTheme.propPink
|
|
consolidatedTheme.propRed = fixedTheme.propRed
|
|
}
|
|
|
|
document.documentElement.style.setProperty('--prop-default', consolidatedTheme.propDefault)
|
|
document.documentElement.style.setProperty('--prop-gray', consolidatedTheme.propGray)
|
|
document.documentElement.style.setProperty('--prop-brown', consolidatedTheme.propBrown)
|
|
document.documentElement.style.setProperty('--prop-orange', consolidatedTheme.propOrange)
|
|
document.documentElement.style.setProperty('--prop-yellow', consolidatedTheme.propYellow)
|
|
document.documentElement.style.setProperty('--prop-green', consolidatedTheme.propGreen)
|
|
document.documentElement.style.setProperty('--prop-blue', consolidatedTheme.propBlue)
|
|
document.documentElement.style.setProperty('--prop-purple', consolidatedTheme.propPurple)
|
|
document.documentElement.style.setProperty('--prop-pink', consolidatedTheme.propPink)
|
|
document.documentElement.style.setProperty('--prop-red', consolidatedTheme.propRed)
|
|
|
|
return consolidatedTheme
|
|
}
|
|
|
|
export function setMattermostTheme(theme: any): Theme {
|
|
if (!theme) {
|
|
return setTheme(defaultTheme)
|
|
}
|
|
|
|
document.documentElement.style.setProperty('--center-channel-bg-rgb', color(theme.centerChannelBg).rgb().array().join(', '))
|
|
document.documentElement.style.setProperty('--center-channel-color-rgb', color(theme.centerChannelColor).rgb().array().join(', '))
|
|
document.documentElement.style.setProperty('--button-bg-rgb', color(theme.buttonBg).rgb().array().join(', '))
|
|
document.documentElement.style.setProperty('--button-color-rgb', color(theme.buttonColor).rgb().array().join(', '))
|
|
document.documentElement.style.setProperty('--sidebar-bg-rgb', color(theme.sidebarBg).rgb().array().join(', '))
|
|
document.documentElement.style.setProperty('--sidebar-text-rgb', color(theme.sidebarText).rgb().array().join(', '))
|
|
document.documentElement.style.setProperty('--link-color-rgb', theme.linkColor)
|
|
document.documentElement.style.setProperty('--sidebar-text-active-border-rgb', color(theme.sidebarTextActiveBorder).rgb().array().join(', '))
|
|
|
|
return setTheme({
|
|
...defaultTheme,
|
|
mainBg: color(theme.centerChannelBg).rgb().array().join(', '),
|
|
mainFg: color(theme.centerChannelColor).rgb().array().join(', '),
|
|
buttonBg: color(theme.buttonBg).rgb().array().join(', '),
|
|
buttonFg: color(theme.buttonColor).rgb().array().join(', '),
|
|
sidebarBg: color(theme.sidebarBg).rgb().array().join(', '),
|
|
sidebarFg: color(theme.sidebarColor || '#ffffff').rgb().array().join(', '),
|
|
sidebarTextActiveBorder: color(theme.sidebarTextActiveBorder).rgb().array().join(', '),
|
|
link: theme.linkColor,
|
|
})
|
|
}
|
|
|
|
function setActiveThemeName(consolidatedTheme: Theme, theme: Theme | null) {
|
|
if (theme === null) {
|
|
activeThemeName = systemThemeName
|
|
} else if (isEqual(consolidatedTheme, darkTheme)) {
|
|
activeThemeName = darkThemeName
|
|
} else if (isEqual(consolidatedTheme, lightTheme)) {
|
|
activeThemeName = lightThemeName
|
|
} else {
|
|
activeThemeName = defaultThemeName
|
|
}
|
|
}
|
|
|
|
export function loadTheme(): Theme {
|
|
const themeStr = UserSettings.theme
|
|
if (themeStr) {
|
|
try {
|
|
const theme = JSON.parse(themeStr)
|
|
const consolidatedTheme = setTheme(theme)
|
|
setActiveThemeName(consolidatedTheme, theme)
|
|
return consolidatedTheme
|
|
} catch (e) {
|
|
return setTheme(null)
|
|
}
|
|
} else {
|
|
return setTheme(null)
|
|
}
|
|
}
|
|
|
|
export function initThemes(): void {
|
|
const darkThemeMq = window.matchMedia('(prefers-color-scheme: dark)')
|
|
const changeHandler = () => {
|
|
const themeStr = UserSettings.theme
|
|
if (!themeStr) {
|
|
setTheme(null)
|
|
}
|
|
}
|
|
if (darkThemeMq.addEventListener) {
|
|
darkThemeMq.addEventListener('change', changeHandler)
|
|
} else if (darkThemeMq.addListener) {
|
|
// Safari and Mac app support
|
|
darkThemeMq.addListener(changeHandler)
|
|
}
|
|
loadTheme()
|
|
}
|
|
|
|
export function getSelectBaseStyle() {
|
|
return {
|
|
dropdownIndicator: (provided: CSSObject): CSSObject => ({
|
|
...provided,
|
|
display: 'none !important',
|
|
}),
|
|
indicatorSeparator: (provided: CSSObject): CSSObject => ({
|
|
...provided,
|
|
display: 'none',
|
|
}),
|
|
loadingIndicator: (provided: CSSObject): CSSObject => ({
|
|
...provided,
|
|
display: 'none',
|
|
}),
|
|
clearIndicator: (provided: CSSObject): CSSObject => ({
|
|
...provided,
|
|
display: 'none',
|
|
}),
|
|
menu: (provided: CSSObject): CSSObject => ({
|
|
...provided,
|
|
width: 'unset',
|
|
background: 'rgb(var(--center-channel-bg-rgb))',
|
|
}),
|
|
option: (provided: CSSObject, state: { isFocused: boolean }): CSSObject => ({
|
|
...provided,
|
|
background: state.isFocused ? 'rgba(var(--center-channel-color-rgb), 0.1)' : 'rgb(var(--center-channel-bg-rgb))',
|
|
color: state.isFocused ? 'rgb(var(--center-channel-color-rgb))' : 'rgb(var(--center-channel-color-rgb))',
|
|
padding: '2px 8px',
|
|
}),
|
|
control: (): CSSObject => ({
|
|
border: 0,
|
|
width: '100%',
|
|
margin: '4px 0 0 0',
|
|
|
|
// display: 'flex',
|
|
// marginTop: 0,
|
|
}),
|
|
valueContainer: (provided: CSSObject): CSSObject => ({
|
|
...provided,
|
|
padding: '0 5px',
|
|
overflow: 'unset',
|
|
}),
|
|
singleValue: (provided: CSSObject): CSSObject => ({
|
|
...provided,
|
|
color: 'rgb(var(--center-channel-color-rgb))',
|
|
overflow: 'unset',
|
|
maxWidth: 'calc(100% - 20px)',
|
|
}),
|
|
input: (provided: CSSObject): CSSObject => ({
|
|
...provided,
|
|
paddingBottom: 0,
|
|
paddingTop: 0,
|
|
marginBottom: 0,
|
|
marginTop: 0,
|
|
}),
|
|
menuList: (provided: CSSObject): CSSObject => ({
|
|
...provided,
|
|
overflowY: 'auto',
|
|
overflowX: 'hidden',
|
|
}),
|
|
}
|
|
}
|
|
|
|
export function getActiveThemeName(): string {
|
|
return activeThemeName || defaultThemeName
|
|
}
|