From 22f6689f2306db619ec669497371bc92d6154ab4 Mon Sep 17 00:00:00 2001 From: Alperen Elhan Date: Thu, 3 Nov 2022 18:01:07 +0300 Subject: [PATCH] feat: add dark mode --- package.json | 1 + pnpm-lock.yaml | 18 +++ src/components/addManga/form.tsx | 2 +- src/components/addManga/index.tsx | 8 +- src/components/addManga/mangaSearchResult.tsx | 114 ++++++++++-------- src/components/headerSearch.tsx | 18 ++- src/components/madeWith.tsx | 2 +- src/components/mangaCard.tsx | 4 +- src/components/navbar.tsx | 4 +- src/pages/_app.tsx | 78 ++++++++---- 10 files changed, 163 insertions(+), 86 deletions(-) diff --git a/package.json b/package.json index add5b12..ceaec78 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,7 @@ "@trpc/server": "^10.0.0-proxy-beta.23", "bullmq": "^2.3.2", "contrast-color": "^1.0.1", + "cookies-next": "^2.1.1", "cron-parser": "^4.6.0", "cronstrue": "^2.14.0", "dayjs": "^1.11.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8b87f45..7fbb340 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -34,6 +34,7 @@ specifiers: autoprefixer: ^10.4.12 bullmq: ^2.3.2 contrast-color: ^1.0.1 + cookies-next: ^2.1.1 cron-parser: ^4.6.0 cronstrue: ^2.14.0 dayjs: ^1.11.5 @@ -95,6 +96,7 @@ dependencies: '@trpc/server': 10.0.0-rc.0 bullmq: 2.4.0 contrast-color: 1.0.1 + cookies-next: 2.1.1 cron-parser: 4.6.0 cronstrue: 2.14.0 dayjs: 1.11.6 @@ -1288,6 +1290,10 @@ packages: resolution: {integrity: sha512-tQA3wqkn7zb0dpgSxjtTEShUsWEExOBWOQ3ByiUT3SzrMM5wEa55NnoGI9X3671Tf19vDQbU/Co2dWVniGqBpQ==} dev: true + /@types/cookie/0.4.1: + resolution: {integrity: sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==} + dev: false + /@types/express-serve-static-core/4.17.31: resolution: {integrity: sha512-DxMhY+NAsTwMMFHBTtJFNp5qiHKJ7TeqOo23zVEM9alT1Ml27Q3xcTH0xwxn7Q0BbMcVEJOs/7aQtUWupUQN3Q==} dependencies: @@ -1332,6 +1338,10 @@ packages: resolution: {integrity: sha512-Y6S38pFr04yb13qqHf8uk1nHE3lXgQ30WZbv1mLliV9pt0NjvqdWttLcrOYLnXbOafknVYRHZGoMSpR9UwfYow==} dev: true + /@types/node/16.18.3: + resolution: {integrity: sha512-jh6m0QUhIRcZpNv7Z/rpN+ZWXOicUUQbSoWks7Htkbb9IjFQj4kzcX/xFCkjstCj5flMsN8FiSvt+q+Tcs4Llg==} + dev: false + /@types/node/18.11.2: resolution: {integrity: sha512-BWN3M23gLO2jVG8g/XHIRFWiiV4/GckeFIqbU/C4V3xpoBBWSMk4OZomouN0wCkfQFPqgZikyLr7DOYDysIkkw==} dev: true @@ -2212,6 +2222,14 @@ packages: engines: {node: '>= 0.6'} dev: false + /cookies-next/2.1.1: + resolution: {integrity: sha512-AZGZPdL1hU3jCjN2UMJTGhLOYzNUN9Gm+v8BdptYIHUdwz397Et1p+sZRfvAl8pKnnmMdX2Pk9xDRKCGBum6GA==} + dependencies: + '@types/cookie': 0.4.1 + '@types/node': 16.18.3 + cookie: 0.4.2 + dev: false + /copy-anything/3.0.2: resolution: {integrity: sha512-CzATjGXzUQ0EvuvgOCI6A4BGOo2bcVx8B+eC2nF862iv9fopnPQwlrbACakNCHRIJbCSBj+J/9JeDf60k64MkA==} engines: {node: '>=12.13'} diff --git a/src/components/addManga/form.tsx b/src/components/addManga/form.tsx index 90101e7..bdcd306 100644 --- a/src/components/addManga/form.tsx +++ b/src/components/addManga/form.tsx @@ -21,7 +21,7 @@ const useStyles = createStyles((theme) => ({ right: '55px', width: 'calc(100% - 55px)', height: '50px', - background: 'white', + background: theme.colorScheme === 'dark' ? theme.colors.dark[7] : theme.white, }, })); diff --git a/src/components/addManga/index.tsx b/src/components/addManga/index.tsx index 31368e6..249f8fb 100644 --- a/src/components/addManga/index.tsx +++ b/src/components/addManga/index.tsx @@ -11,7 +11,7 @@ const useStyles = createStyles((theme) => ({ display: 'flex', justifyContent: 'center', alignItems: 'center', - backgroundColor: theme.colors.gray[4], + backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark[4] : theme.colors.gray[4], cursor: 'pointer', transition: 'transform 150ms ease, box-shadow 150ms ease', @@ -21,6 +21,10 @@ const useStyles = createStyles((theme) => ({ boxShadow: theme.shadows.md, }, }, + + plusIcon: { + color: theme.colorScheme === 'light' ? theme.colors.dark[4] : theme.colors.gray[4], + }, })); export const useAddMangaModal = () => { @@ -58,7 +62,7 @@ export function AddManga({ onAdd }: { onAdd: () => void }) { return ( addMangaModal(onAdd)}> - + ); diff --git a/src/components/addManga/mangaSearchResult.tsx b/src/components/addManga/mangaSearchResult.tsx index 0d222e8..7fedf71 100644 --- a/src/components/addManga/mangaSearchResult.tsx +++ b/src/components/addManga/mangaSearchResult.tsx @@ -1,34 +1,40 @@ -import { createStyles, Image, SimpleGrid, Text, UnstyledButton } from '@mantine/core'; +import { createStyles, Image, ScrollArea, SimpleGrid, Text, UnstyledButton } from '@mantine/core'; import { useUncontrolled } from '@mantine/hooks'; import { useEffect, useState } from 'react'; -const useStyles = createStyles((theme, { checked, disabled }: { checked: boolean; disabled: boolean }) => ({ - button: { - display: 'flex', - alignItems: 'center', - width: '100%', - transition: 'background-color 150ms ease, border-color 150ms ease', - border: `1px solid ${ - checked - ? theme.fn.variant({ variant: 'outline', color: theme.primaryColor }).border - : theme.colorScheme === 'dark' - ? theme.colors.dark[8] - : theme.colors.gray[3] - }`, - borderRadius: theme.radius.sm, - padding: theme.spacing.sm, - backgroundColor: checked - ? theme.fn.variant({ variant: 'light', color: theme.primaryColor }).background - : disabled - ? theme.colors.gray[3] - : theme.white, - }, +const useStyles = createStyles((theme, { checked, disabled }: { checked: boolean; disabled: boolean }) => { + let backgroundColor = 'light'; + if (disabled) { + backgroundColor = theme.colorScheme === 'dark' ? theme.colors.dark[4] : theme.colors.gray[4]; + } + if (checked) { + backgroundColor = theme.fn.variant({ variant: 'light', color: theme.primaryColor }).background!; + } + return { + button: { + display: 'flex', + alignItems: 'center', + width: '100%', + transition: 'background-color 150ms ease, border-color 150ms ease', + border: `1px solid ${ + checked + ? theme.fn.variant({ variant: 'outline', color: theme.primaryColor }).border + : theme.colorScheme === 'dark' + ? theme.colors.dark[8] + : theme.colors.gray[3] + }`, + borderRadius: theme.radius.sm, + padding: theme.spacing.sm, + backgroundColor, + outline: 'none !important', + }, - body: { - flex: 1, - marginLeft: theme.spacing.md, - }, -})); + body: { + flex: 1, + marginLeft: theme.spacing.md, + }, + }; +}); interface ImageCheckboxProps { checked?: boolean; @@ -115,31 +121,33 @@ export function MangaSearchResult({ }, [items]); return ( - - {items.map((m) => ( - { - if (checked) { - setSelected(m); - onSelect(m); - } else { - setSelected(undefined); - onSelect(undefined); - } - }} - /> - ))} - + + + {items.map((m) => ( + { + if (checked) { + setSelected(m); + onSelect(m); + } else { + setSelected(undefined); + onSelect(undefined); + } + }} + /> + ))} + + ); } diff --git a/src/components/headerSearch.tsx b/src/components/headerSearch.tsx index 02aad2e..1cdc515 100644 --- a/src/components/headerSearch.tsx +++ b/src/components/headerSearch.tsx @@ -17,6 +17,16 @@ const useStyles = createStyles((theme) => ({ backgroundColor: theme.white, cursor: 'pointer', outline: '0 !important', + + '&:hover': { + backgroundColor: theme.colors.gray[0], + }, + }, + + kbd: { + backgroundColor: theme.colors.gray[4], + borderColor: theme.colors.gray[4], + color: theme.black, }, })); @@ -87,7 +97,13 @@ export function SearchControl() { - Ctrl+P + + Ctrl + + + + + P + diff --git a/src/components/madeWith.tsx b/src/components/madeWith.tsx index 452327b..64df8b2 100644 --- a/src/components/madeWith.tsx +++ b/src/components/madeWith.tsx @@ -36,7 +36,7 @@ export function MadeWith({ minimized }: { minimized: boolean }): JSX.Element { {hearth} in{' '} - + a.k.a. Turkey} inline position="top-start"> Isekai diff --git a/src/components/mangaCard.tsx b/src/components/mangaCard.tsx index b4b7bbf..537c728 100644 --- a/src/components/mangaCard.tsx +++ b/src/components/mangaCard.tsx @@ -59,12 +59,14 @@ const useStyles = createStyles((theme, _params, getRef) => ({ }, editButton: { ref: getRef('editButton'), + backgroundColor: theme.white, + color: theme.colors.blue[6], position: 'absolute', right: 10, bottom: 10, display: 'none', '&:hover': { - backgroundColor: theme.white, + backgroundColor: theme.colors.gray[0], }, }, title: { diff --git a/src/components/navbar.tsx b/src/components/navbar.tsx index b7f692f..f711600 100644 --- a/src/components/navbar.tsx +++ b/src/components/navbar.tsx @@ -59,10 +59,10 @@ const useStyles = createStyles((theme) => ({ paddingTop: theme.spacing.xs, paddingBottom: theme.spacing.xs, borderRadius: theme.radius.sm, - color: theme.colors.gray[7], + color: theme.colorScheme === 'dark' ? theme.colors.gray[5] : theme.colors.gray[8], '&:hover': { - backgroundColor: theme.colors.gray[0], + backgroundColor: theme.colorScheme === 'dark' ? theme.colors.gray[9] : theme.colors.gray[0], }, }, })); diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index a3f37f9..9f66b5f 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -1,14 +1,40 @@ -import { AppShell, MantineProvider } from '@mantine/core'; +import { AppShell, ColorScheme, ColorSchemeProvider, MantineProvider } from '@mantine/core'; +import { useColorScheme, useHotkeys } from '@mantine/hooks'; import { ModalsProvider } from '@mantine/modals'; import { NotificationsProvider } from '@mantine/notifications'; +import { getCookie, setCookie } from 'cookies-next'; import type { AppProps } from 'next/app'; import Head from 'next/head'; +import { useEffect, useState } from 'react'; import { KaizokuHeader } from '../components/header'; import { KaizokuNavbar } from '../components/navbar'; import '../styles/globals.css'; import { trpc } from '../utils/trpc'; -function MyApp({ Component, pageProps }: AppProps) { +function MyApp(props: AppProps) { + const { Component, pageProps } = props; + const preferredColorScheme = useColorScheme(); + const [colorScheme, setColorScheme] = useState('light'); + useEffect(() => { + let followSystem = getCookie('follow-system'); + if (followSystem === undefined) { + followSystem = true; + setCookie('follow-system', '1'); + } + if (followSystem === '1') { + setColorScheme(preferredColorScheme); + } else { + setColorScheme((getCookie('mantine-color-scheme') as ColorScheme) || preferredColorScheme); + } + }, [preferredColorScheme]); + const toggleColorScheme = (value?: ColorScheme) => { + const nextColorScheme = value || (colorScheme === 'dark' ? 'light' : 'dark'); + setColorScheme(nextColorScheme); + setCookie('mantine-color-scheme', nextColorScheme, { maxAge: 60 * 60 * 24 * 30 }); + }; + + useHotkeys([['shift+t', () => toggleColorScheme()]]); + return ( <> @@ -16,29 +42,31 @@ function MyApp({ Component, pageProps }: AppProps) { - - - - } - header={} - styles={(theme) => ({ - main: { backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark[8] : theme.colors.gray[0] }, - })} - > - - - - - + + + + + } + header={} + styles={(theme) => ({ + main: { backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark[8] : theme.colors.gray[0] }, + })} + > + + + + + + ); }