feat: add theme hook and change color of stats chart

This commit is contained in:
moonrailgun 2023-09-23 20:44:26 +08:00
parent c180d1d5f1
commit 72e3753503
4 changed files with 133 additions and 23 deletions

View File

@ -22,6 +22,7 @@
"axios": "^1.5.0", "axios": "^1.5.0",
"bcryptjs": "^2.4.3", "bcryptjs": "^2.4.3",
"clsx": "^2.0.0", "clsx": "^2.0.0",
"colord": "^2.9.3",
"compose-middleware": "^5.0.1", "compose-middleware": "^5.0.1",
"compression": "^1.7.4", "compression": "^1.7.4",
"dayjs": "^1.11.9", "dayjs": "^1.11.9",

View File

@ -28,6 +28,9 @@ dependencies:
clsx: clsx:
specifier: ^2.0.0 specifier: ^2.0.0
version: 2.0.0 version: 2.0.0
colord:
specifier: ^2.9.3
version: 2.9.3
compose-middleware: compose-middleware:
specifier: ^5.0.1 specifier: ^5.0.1
version: 5.0.1 version: 5.0.1
@ -2519,6 +2522,10 @@ packages:
color-string: 1.9.1 color-string: 1.9.1
dev: false dev: false
/colord@2.9.3:
resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==}
dev: false
/combined-stream@1.0.8: /combined-stream@1.0.8:
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
engines: {node: '>= 0.8'} engines: {node: '>= 0.8'}

View File

@ -22,6 +22,7 @@ import {
import { useEvent } from '../hooks/useEvent'; import { useEvent } from '../hooks/useEvent';
import { MetricCard } from './MetricCard'; import { MetricCard } from './MetricCard';
import { formatNumber, formatShortTime } from '../utils/common'; import { formatNumber, formatShortTime } from '../utils/common';
import { useTheme } from '../hooks/useTheme';
interface WebsiteOverviewProps { interface WebsiteOverviewProps {
workspaceId: string; workspaceId: string;
@ -212,31 +213,35 @@ export const StatsChart: React.FC<{
data: { x: string; y: number; type: string }[]; data: { x: string; y: number; type: string }[];
unit: DateUnit; unit: DateUnit;
}> = React.memo((props) => { }> = React.memo((props) => {
const config: ColumnConfig = useMemo( const { colors } = useTheme();
() => ({
data: props.data, const config = useMemo(
isStack: true, () =>
xField: 'x', ({
yField: 'y', data: props.data,
seriesField: 'type', isStack: true,
label: { xField: 'x',
position: 'middle' as const, yField: 'y',
style: { seriesField: 'type',
fill: '#FFFFFF',
opacity: 0.6,
},
},
tooltip: {
title: (t) => formatDate(t),
},
xAxis: {
label: { label: {
autoHide: true, position: 'middle' as const,
autoRotate: false, style: {
formatter: (text) => formatDateWithUnit(text, props.unit), fill: '#FFFFFF',
opacity: 0.6,
},
}, },
}, tooltip: {
}), title: (t) => formatDate(t),
},
color: [colors.chart.pv, colors.chart.uv],
xAxis: {
label: {
autoHide: true,
autoRotate: false,
formatter: (text) => formatDateWithUnit(text, props.unit),
},
},
} as ColumnConfig),
[props.data, props.unit] [props.data, props.unit]
); );

View File

@ -0,0 +1,97 @@
import { useEffect, useMemo } from 'react';
import { colord } from 'colord';
const THEME_CONFIG = 'tianji.theme';
const THEME_COLORS = {
light: {
primary: '#2680eb',
gray50: '#ffffff',
gray75: '#fafafa',
gray100: '#f5f5f5',
gray200: '#eaeaea',
gray300: '#e1e1e1',
gray400: '#cacaca',
gray500: '#b3b3b3',
gray600: '#8e8e8e',
gray700: '#6e6e6e',
gray800: '#4b4b4b',
gray900: '#2c2c2c',
},
dark: {
primary: '#2680eb',
gray50: '#252525',
gray75: '#2f2f2f',
gray100: '#323232',
gray200: '#3e3e3e',
gray300: '#4a4a4a',
gray400: '#5a5a5a',
gray500: '#6e6e6e',
gray600: '#909090',
gray700: '#b9b9b9',
gray800: '#e3e3e3',
gray900: '#ffffff',
},
};
type ValidTheme = keyof typeof THEME_COLORS;
function isValidTheme(theme: string | null): theme is ValidTheme {
if (!theme) {
return false;
}
return ['light', 'dark'].includes(theme);
}
export function useTheme() {
const defaultTheme: ValidTheme =
typeof window !== 'undefined'
? window?.matchMedia('(prefers-color-scheme: dark)')?.matches
? 'dark'
: 'light'
: 'light';
const customTheme = window.localStorage.getItem(THEME_CONFIG);
const theme = isValidTheme(customTheme) ? customTheme : defaultTheme;
const primaryColor = useMemo(() => colord(THEME_COLORS[theme].primary), []);
const colors = useMemo(
() => ({
theme: {
...THEME_COLORS[theme],
},
chart: {
text: THEME_COLORS[theme].gray700,
line: THEME_COLORS[theme].gray200,
pv: primaryColor.alpha(0.4).toRgbString(),
uv: primaryColor.alpha(0.6).toRgbString(),
},
map: {
baseColor: THEME_COLORS[theme].primary,
fillColor: THEME_COLORS[theme].gray100,
strokeColor: THEME_COLORS[theme].primary,
hoverColor: THEME_COLORS[theme].primary,
},
}),
[]
);
function saveTheme(value: string) {
window.localStorage.setItem(THEME_CONFIG, value);
}
useEffect(() => {
document.body.setAttribute('data-theme', theme);
}, [theme]);
useEffect(() => {
const url = new URL(window?.location?.href);
const theme = url.searchParams.get('theme');
if (theme && ['light', 'dark'].includes(theme)) {
saveTheme(theme);
}
}, []);
return { theme, saveTheme, colors };
}