Updated theme handling.

This commit is contained in:
Mike Cao 2025-04-01 22:58:38 -05:00
parent c71e9b5707
commit 34a8fa100c
10 changed files with 57 additions and 93 deletions

View file

@ -1,17 +1,16 @@
import classNames from 'classnames';
import { Button, Icon } from '@umami/react-zen';
import { useTheme } from '@/components/hooks';
import { Button, Icon, useTheme } from '@umami/react-zen';
import { Icons } from '@/components/icons';
import styles from './ThemeSetting.module.css';
export function ThemeSetting() {
const { theme, saveTheme } = useTheme();
const { theme, setTheme } = useTheme();
return (
<div className={styles.buttons}>
<Button
className={classNames({ [styles.active]: theme === 'light' })}
onPress={() => saveTheme('light')}
onPress={() => setTheme('light')}
>
<Icon>
<Icons.Sun />
@ -19,7 +18,7 @@ export function ThemeSetting() {
</Button>
<Button
className={classNames({ [styles.active]: theme === 'dark' })}
onPress={() => saveTheme('dark')}
onPress={() => setTheme('dark')}
>
<Icon>
<Icons.Moon />

View file

@ -1,8 +1,9 @@
import { useMemo, useState } from 'react';
import { useTheme } from '@umami/react-zen';
import { BarChartTooltip } from '@/components/charts/BarChartTooltip';
import { Chart, ChartProps } from '@/components/charts/Chart';
import { useTheme } from '@/components/hooks';
import { renderNumberLabels } from '@/lib/charts';
import { getThemeColors } from '@/lib/colors';
export interface BarChartProps extends ChartProps {
unit: string;
@ -19,7 +20,8 @@ export interface BarChartProps extends ChartProps {
export function BarChart(props: BarChartProps) {
const [tooltip, setTooltip] = useState(null);
const { colors } = useTheme();
const { theme } = useTheme();
const { colors } = getThemeColors(theme);
const {
renderXLabel,
renderYLabel,

View file

@ -2,15 +2,5 @@ import { Box } from '@umami/react-zen';
import type { BoxProps } from '@umami/react-zen/Box';
export function Panel(props: BoxProps) {
return (
<Box
padding="6"
border
borderRadius="3"
backgroundColor
shadow="4"
position="relative"
{...props}
/>
);
return <Box padding="6" border borderRadius="3" backgroundColor position="relative" {...props} />;
}

View file

@ -44,5 +44,4 @@ export * from './useRegionNames';
export * from './useReport';
export * from './useSticky';
export * from './useNavigation';
export * from './useTheme';
export * from './useTimezone';

View file

@ -8,13 +8,13 @@ export function useLoginQuery(): {
user: any;
setUser: (data: any) => void;
} & UseQueryResult {
const { get, useQuery } = useApi();
const { post, useQuery } = useApi();
const user = useApp(selector);
const query = useQuery({
queryKey: ['login'],
queryFn: async () => {
const data = await get('/auth/verify');
const data = await post('/auth/verify');
setUser(data);

View file

@ -1,63 +0,0 @@
import { useEffect, useMemo } from 'react';
import { useApp, setTheme } from '@/store/app';
import { getItem, setItem } from '@/lib/storage';
import { DEFAULT_THEME, THEME_COLORS, THEME_CONFIG } from '@/lib/constants';
import { colord } from 'colord';
const selector = (state: { theme: string }) => state.theme;
export function useTheme() {
const theme = useApp(selector) || getItem(THEME_CONFIG) || DEFAULT_THEME;
const { primary, text, line, fill } = THEME_COLORS[theme];
const primaryColor = colord(THEME_COLORS[theme].primary);
const colors = useMemo(() => {
return {
theme: {
...THEME_COLORS[theme],
},
chart: {
text,
line,
views: {
hoverBackgroundColor: primaryColor.alpha(0.7).toRgbString(),
backgroundColor: primaryColor.alpha(0.4).toRgbString(),
borderColor: primaryColor.alpha(0.7).toRgbString(),
hoverBorderColor: primaryColor.toRgbString(),
},
visitors: {
hoverBackgroundColor: primaryColor.alpha(0.9).toRgbString(),
backgroundColor: primaryColor.alpha(0.6).toRgbString(),
borderColor: primaryColor.alpha(0.9).toRgbString(),
hoverBorderColor: primaryColor.toRgbString(),
},
},
map: {
baseColor: primary,
fillColor: fill,
strokeColor: primary,
hoverColor: primary,
},
};
}, [theme]);
const saveTheme = (value: string) => {
setItem(THEME_CONFIG, value);
setTheme(value);
};
useEffect(() => {
document.body.setAttribute('data-theme', theme);
}, [theme]);
useEffect(() => {
const url = new URL(window?.location?.href);
const theme = url.searchParams.get('theme');
if (['light', 'dark'].includes(theme)) {
saveTheme(theme);
}
}, []);
return { theme, saveTheme, colors };
}

View file

@ -1,7 +1,9 @@
import { useMemo } from 'react';
import { useTheme } from '@umami/react-zen';
import { BarChart, BarChartProps } from '@/components/charts/BarChart';
import { useLocale, useTheme, useMessages } from '@/components/hooks';
import { useLocale, useMessages } from '@/components/hooks';
import { renderDateLabels } from '@/lib/charts';
import { getThemeColors } from '@/lib/colors';
export interface PageviewsChartProps extends BarChartProps {
data: {
@ -25,7 +27,8 @@ export function PageviewsChart({
...props
}: PageviewsChartProps) {
const { formatMessage, labels } = useMessages();
const { colors } = useTheme();
const { theme } = useTheme();
const { colors } = getThemeColors(theme);
const { locale } = useLocale();
const chartData = useMemo(() => {

View file

@ -1,4 +1,4 @@
import { FloatingTooltip, Column } from '@umami/react-zen';
import { FloatingTooltip, Column, useTheme } from '@umami/react-zen';
import { useState, useMemo, HTMLAttributes } from 'react';
import { ComposableMap, Geographies, Geography, ZoomableGroup } from 'react-simple-maps';
import classNames from 'classnames';
@ -6,7 +6,6 @@ import { colord } from 'colord';
import { ISO_COUNTRIES, MAP_FILE } from '@/lib/constants';
import {
useDateRange,
useTheme,
useWebsiteMetricsQuery,
useCountryNames,
useLocale,
@ -15,6 +14,7 @@ import {
import { formatLongNumber } from '@/lib/format';
import { percentFilter } from '@/lib/filters';
import styles from './WorldMap.module.css';
import { getThemeColors } from '@/lib/colors';
export function WorldMap({
websiteId,
@ -27,7 +27,8 @@ export function WorldMap({
className?: string;
} & HTMLAttributes<HTMLDivElement>) {
const [tooltip, setTooltipPopup] = useState();
const { theme, colors } = useTheme();
const { theme } = useTheme();
const { colors } = getThemeColors(theme);
const { locale } = useLocale();
const { formatMessage, labels } = useMessages();
const { countryNames } = useCountryNames(locale);

View file

@ -1,4 +1,6 @@
import md5 from 'md5';
import { THEME_COLORS } from '@/lib/constants';
import { colord } from 'colord';
export const pick = (num: number, arr: any[]) => {
return arr[num % arr.length];
@ -43,3 +45,38 @@ export function getColor(seed: string, min: number = 0, max: number = 255) {
return rgb2Hex(r, g, b);
}
export function getThemeColors(theme: string) {
const { primary, text, line, fill } = THEME_COLORS[theme];
const primaryColor = colord(THEME_COLORS[theme].primary);
return {
colors: {
theme: {
...THEME_COLORS[theme],
},
chart: {
text,
line,
views: {
hoverBackgroundColor: primaryColor.alpha(0.7).toRgbString(),
backgroundColor: primaryColor.alpha(0.4).toRgbString(),
borderColor: primaryColor.alpha(0.7).toRgbString(),
hoverBorderColor: primaryColor.toRgbString(),
},
visitors: {
hoverBackgroundColor: primaryColor.alpha(0.9).toRgbString(),
backgroundColor: primaryColor.alpha(0.6).toRgbString(),
borderColor: primaryColor.alpha(0.9).toRgbString(),
hoverBorderColor: primaryColor.toRgbString(),
},
},
map: {
baseColor: primary,
fillColor: fill,
strokeColor: primary,
hoverColor: primary,
},
},
};
}

View file

@ -31,10 +31,6 @@ const initialState = {
const store = create(() => ({ ...initialState }));
export function setTheme(theme: string) {
store.setState({ theme });
}
export function setTimezone(timezone: string) {
store.setState({ timezone });
}