feat: add theme hook and change color of stats chart
This commit is contained in:
parent
c180d1d5f1
commit
72e3753503
@ -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",
|
||||||
|
@ -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'}
|
||||||
|
@ -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]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
97
src/client/hooks/useTheme.ts
Normal file
97
src/client/hooks/useTheme.ts
Normal 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 };
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user