feat: add dark mode
This commit is contained in:
parent
626c2a2fa6
commit
dc3bbe0b12
@ -67,6 +67,7 @@
|
||||
"react-dom": "^18.2.0",
|
||||
"react-easy-sort": "^1.5.3",
|
||||
"react-grid-layout": "1.4.2",
|
||||
"react-icons": "^4.12.0",
|
||||
"react-resizable": "^3.0.5",
|
||||
"react-router": "^6.15.0",
|
||||
"react-router-dom": "^6.15.0",
|
||||
|
@ -142,6 +142,9 @@ dependencies:
|
||||
react-grid-layout:
|
||||
specifier: 1.4.2
|
||||
version: 1.4.2(react-dom@18.2.0)(react@18.2.0)
|
||||
react-icons:
|
||||
specifier: ^4.12.0
|
||||
version: 4.12.0(react@18.2.0)
|
||||
react-resizable:
|
||||
specifier: ^3.0.5
|
||||
version: 3.0.5(react-dom@18.2.0)(react@18.2.0)
|
||||
@ -6932,6 +6935,14 @@ packages:
|
||||
resize-observer-polyfill: 1.5.1
|
||||
dev: false
|
||||
|
||||
/react-icons@4.12.0(react@18.2.0):
|
||||
resolution: {integrity: sha512-IBaDuHiShdZqmfc/TwHu6+d6k2ltNCf3AszxNmjJc1KUfXdEeRJOKyNvLmAHaarhzGmTSVygNdyu8/opXv2gaw==}
|
||||
peerDependencies:
|
||||
react: '*'
|
||||
dependencies:
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
/react-is@16.13.1:
|
||||
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
|
||||
dev: false
|
||||
|
@ -15,6 +15,9 @@ import { MonitorPage } from './pages/Monitor';
|
||||
import { WebsitePage } from './pages/Website';
|
||||
import { useGlobalConfig } from './hooks/useConfig';
|
||||
import { useInjectWebsiteScript } from './hooks/useInjectWebsiteScript';
|
||||
import { ConfigProvider, theme } from 'antd';
|
||||
import { useGlobalStateStore } from './store/global';
|
||||
import clsx from 'clsx';
|
||||
|
||||
export const AppRoutes: React.FC = React.memo(() => {
|
||||
const { info } = useUserStore();
|
||||
@ -51,14 +54,24 @@ export const AppRoutes: React.FC = React.memo(() => {
|
||||
AppRoutes.displayName = 'AppRoutes';
|
||||
|
||||
export const App: React.FC = React.memo(() => {
|
||||
const colorScheme = useGlobalStateStore((state) => state.colorScheme);
|
||||
const algorithm =
|
||||
colorScheme === 'dark' ? theme.darkAlgorithm : theme.defaultAlgorithm;
|
||||
|
||||
return (
|
||||
<div className="App">
|
||||
<div
|
||||
className={clsx('App', {
|
||||
dark: colorScheme === 'dark',
|
||||
})}
|
||||
>
|
||||
<trpc.Provider client={trpcClient} queryClient={queryClient}>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<BrowserRouter>
|
||||
<TokenLoginContainer>
|
||||
<AppRoutes />
|
||||
</TokenLoginContainer>
|
||||
<ConfigProvider theme={{ algorithm }}>
|
||||
<TokenLoginContainer>
|
||||
<AppRoutes />
|
||||
</TokenLoginContainer>
|
||||
</ConfigProvider>
|
||||
</BrowserRouter>
|
||||
</QueryClientProvider>
|
||||
</trpc.Provider>
|
||||
|
30
src/client/components/ColorSchemeSwitcher.tsx
Normal file
30
src/client/components/ColorSchemeSwitcher.tsx
Normal file
@ -0,0 +1,30 @@
|
||||
import React from 'react';
|
||||
import { useGlobalStateStore } from '../store/global';
|
||||
import { LuMoon, LuSun } from 'react-icons/lu';
|
||||
import { useEvent } from '../hooks/useEvent';
|
||||
import { Button } from 'antd';
|
||||
|
||||
export const ColorSchemeSwitcher: React.FC = React.memo(() => {
|
||||
const colorScheme = useGlobalStateStore((state) => state.colorScheme);
|
||||
const handleSwitchColorScheme = useEvent(() => {
|
||||
useGlobalStateStore.setState({
|
||||
colorScheme: colorScheme === 'dark' ? 'light' : 'dark',
|
||||
});
|
||||
});
|
||||
|
||||
return (
|
||||
<Button
|
||||
icon={
|
||||
colorScheme === 'dark' ? (
|
||||
<LuMoon className="anticon" />
|
||||
) : (
|
||||
<LuSun className="anticon" />
|
||||
)
|
||||
}
|
||||
shape="circle"
|
||||
size="large"
|
||||
onClick={handleSwitchColorScheme}
|
||||
/>
|
||||
);
|
||||
});
|
||||
ColorSchemeSwitcher.displayName = 'ColorSchemeSwitcher';
|
@ -14,8 +14,8 @@ export const NavItem: React.FC<{
|
||||
<Link to={props.to}>
|
||||
<div
|
||||
className={clsx('leading-[3.75rem] border-b-2', {
|
||||
'text-gray-950 border-blue-500': isCurrent,
|
||||
'text-gray-900 border-transparent hover:text-gray-950 hover:border-blue-500':
|
||||
'text-gray-950 dark:text-gray-200 border-blue-500': isCurrent,
|
||||
'text-gray-900 dark:text-gray-400 border-transparent hover:text-gray-950 hover:border-blue-500':
|
||||
!isCurrent,
|
||||
})}
|
||||
>
|
||||
|
@ -49,7 +49,7 @@ export const Dashboard: React.FC = React.memo(() => {
|
||||
<div className="py-4">
|
||||
<div
|
||||
className={clsx(
|
||||
'flex gap-2 justify-end bg-white py-2',
|
||||
'flex gap-2 justify-end bg-white dark:bg-gray-900 py-2',
|
||||
isEditMode && 'sticky top-0 z-10'
|
||||
)}
|
||||
>
|
||||
|
@ -47,7 +47,7 @@ export const MonitorEventList: React.FC<MonitorEventListProps> = React.memo(
|
||||
{item.type}
|
||||
</div>
|
||||
|
||||
<div className="text-black text-opacity-60">
|
||||
<div className="text-black text-opacity-60 dark:text-white dark:text-opacity-60">
|
||||
{dayjs(item.createdAt).format('YYYY-MM-DD HH:mm:ss')}
|
||||
</div>
|
||||
|
||||
|
@ -5,6 +5,7 @@ import { UserOutlined } from '@ant-design/icons';
|
||||
import { Button, Dropdown } from 'antd';
|
||||
import { useUserStore } from '../store/user';
|
||||
import { useLogout } from '../api/model/user';
|
||||
import { ColorSchemeSwitcher } from '../components/ColorSchemeSwitcher';
|
||||
|
||||
export const Layout: React.FC = React.memo(() => {
|
||||
const [params] = useSearchParams();
|
||||
@ -25,12 +26,12 @@ export const Layout: React.FC = React.memo(() => {
|
||||
const showHeader = !params.has('hideHeader');
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full">
|
||||
<div className="flex flex-col h-full dark:bg-gray-900 dark:text-gray-300">
|
||||
{showHeader && (
|
||||
<div className="flex items-center bg-gray-100 px-4 sticky top-0 z-20">
|
||||
<div className="flex items-center bg-gray-100 dark:bg-gray-800 px-4 sticky top-0 z-20">
|
||||
<div className="px-2 mr-10 font-bold flex items-center">
|
||||
<img src="/icon.svg" className="w-10 h-10 mr-2" />
|
||||
<span className="text-xl">Tianji</span>
|
||||
<span className="text-xl dark:text-gray-200">Tianji</span>
|
||||
</div>
|
||||
<div className="flex gap-8">
|
||||
<NavItem to="/dashboard" label="Dashboard" />
|
||||
@ -42,7 +43,9 @@ export const Layout: React.FC = React.memo(() => {
|
||||
|
||||
<div className="flex-1" />
|
||||
|
||||
<div>
|
||||
<div className="flex gap-2">
|
||||
<ColorSchemeSwitcher />
|
||||
|
||||
<Dropdown
|
||||
placement="bottomRight"
|
||||
menu={{
|
||||
|
@ -14,7 +14,7 @@ export const MonitorPage: React.FC = React.memo(() => {
|
||||
<div>
|
||||
<div className="px-4 pt-4">
|
||||
<div
|
||||
className="px-3 py-2 rounded-full bg-green-400 hover:bg-green-500 text-white inline-block cursor-pointer"
|
||||
className="px-3 py-2 rounded-full bg-green-400 hover:bg-green-500 text-white dark:text-gray-700 inline-block cursor-pointer"
|
||||
onClick={() => navigate('/monitor/add')}
|
||||
>
|
||||
Add new Montior
|
||||
@ -22,7 +22,7 @@ export const MonitorPage: React.FC = React.memo(() => {
|
||||
</div>
|
||||
</div>
|
||||
<div className="py-5 flex flex-1 overflow-hidden">
|
||||
<div className="w-5/12 bg-gray-50">
|
||||
<div className="w-5/12 bg-gray-50 dark:bg-gray-800">
|
||||
<MonitorList />
|
||||
</div>
|
||||
<div className="w-7/12">
|
||||
|
@ -18,10 +18,12 @@ interface GlobalState {
|
||||
dateRange: DateRange;
|
||||
startDate: Dayjs | null;
|
||||
endDate: Dayjs | null;
|
||||
colorScheme: 'light' | 'dark';
|
||||
}
|
||||
|
||||
export const useGlobalStateStore = create<GlobalState>(() => ({
|
||||
dateRange: DateRange.Last24Hours,
|
||||
startDate: null,
|
||||
endDate: null,
|
||||
colorScheme: 'light',
|
||||
}));
|
||||
|
@ -1,11 +0,0 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: ["./index.html", "./src/**/*.{js,jsx,ts,tsx}"],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
corePlugins: {
|
||||
preflight: false,
|
||||
},
|
||||
plugins: [],
|
||||
}
|
19
tailwind.config.ts
Normal file
19
tailwind.config.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import type { Config } from 'tailwindcss';
|
||||
import colors from 'tailwindcss/colors';
|
||||
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: ['./index.html', './src/**/*.{js,jsx,ts,tsx}'],
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
gray: colors.neutral,
|
||||
},
|
||||
},
|
||||
},
|
||||
darkMode: 'class',
|
||||
corePlugins: {
|
||||
preflight: false,
|
||||
},
|
||||
plugins: [],
|
||||
} satisfies Config;
|
Loading…
Reference in New Issue
Block a user