Skip to content

Commit

Permalink
feat: add color mode switch (#6)
Browse files Browse the repository at this point in the history
* feat: add color mode toggle

* refactor: move header to separate component

* chore: squash
  • Loading branch information
kieranroneill authored Feb 22, 2024
1 parent 9a7e6b1 commit d9be770
Show file tree
Hide file tree
Showing 7 changed files with 131 additions and 101 deletions.
4 changes: 2 additions & 2 deletions src/components/Footer/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ const Footer: FC = () => {
<VStack
alignItems="center"
bg={theme.colors.altBackground}
px={DEFAULT_GAP * 2}
px={24}
py={DEFAULT_GAP * 2}
spacing={DEFAULT_GAP + 2}
spacing={10}
w="full"
>
<HStack justifyContent="space-evenly" w="full">
Expand Down
87 changes: 87 additions & 0 deletions src/components/Header/Header.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import {
ColorModeContextType,
HStack,
IconButton,
Spacer,
Tooltip,
useColorMode,
} from '@chakra-ui/react';
import React, { FC } from 'react';
import { useTranslation } from 'react-i18next';
import {
IoArrowBackOutline,
IoMoonOutline,
IoSunnyOutline,
} from 'react-icons/io5';
import {
Location,
NavigateFunction,
useLocation,
useNavigate,
} from 'react-router-dom';

// constants
import { DEFAULT_GAP, SEARCH_ROUTE } from '@app/constants';

// hooks
import useButtonHoverBackgroundColor from '@app/hooks/useButtonHoverBackgroundColor';
import useDefaultTextColor from '@app/hooks/useDefaultTextColor';

const Header: FC = () => {
const { t } = useTranslation();
const location: Location = useLocation();
const navigate: NavigateFunction = useNavigate();
// hooks
const buttonHoverBackgroundColor: string = useButtonHoverBackgroundColor();
const defaultTextColor: string = useDefaultTextColor();
const { colorMode, toggleColorMode }: ColorModeContextType = useColorMode();
// handlers
const handlerBackClick = () =>
navigate(SEARCH_ROUTE, {
replace: true,
});
const handlerColorChangeClick = () => toggleColorMode();

return (
<HStack
alignItems="center"
justifyContent="space-between"
p={DEFAULT_GAP / 2}
w="full"
>
{!location.pathname.includes(SEARCH_ROUTE) && (
<Tooltip label={t('captions.goBackToSearch')}>
<IconButton
_hover={{ bg: buttonHoverBackgroundColor }}
aria-label="Go back"
color={defaultTextColor}
icon={<IoArrowBackOutline />}
onClick={handlerBackClick}
size="lg"
variant="ghost"
/>
</Tooltip>
)}

<Spacer />

<Tooltip
label={t('captions.switchColorMode', {
colorMode: colorMode === 'dark' ? 'light' : 'dark',
})}
>
<IconButton
_hover={{ bg: buttonHoverBackgroundColor }}
aria-label="Change color mode"
color={defaultTextColor}
icon={colorMode === 'dark' ? <IoSunnyOutline /> : <IoMoonOutline />}
onClick={handlerColorChangeClick}
size="lg"
variant="ghost"
/>
</Tooltip>
</HStack>
);
};

export default Header;
1 change: 1 addition & 0 deletions src/components/Header/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './Header';
92 changes: 4 additions & 88 deletions src/components/Layout/Layout.tsx
Original file line number Diff line number Diff line change
@@ -1,38 +1,13 @@
import {
Center,
Flex,
Heading,
HStack,
IconButton,
Text,
Tooltip,
VStack,
} from '@chakra-ui/react';
import { Center, Flex, VStack } from '@chakra-ui/react';
import React, { FC } from 'react';
import { Helmet } from 'react-helmet';
import { useTranslation } from 'react-i18next';
import { IoArrowBackOutline } from 'react-icons/io5';
import {
Location,
NavigateFunction,
useLocation,
useNavigate,
} from 'react-router-dom';

// components
import AVMIcon from '@app/components/AVMIcon';
import Footer from '@app/components/Footer';
import Header from '@app/components/Header';

// constants
import {
BODY_BACKGROUND_COLOR,
DEFAULT_GAP,
SEARCH_ROUTE,
} from '@app/constants';

// hooks
import useButtonHoverBackgroundColor from '@app/hooks/useButtonHoverBackgroundColor';
import useDefaultTextColor from '@app/hooks/useDefaultTextColor';
import { BODY_BACKGROUND_COLOR, DEFAULT_GAP } from '@app/constants';

// selectors
import { useSelectTitle } from '@app/selectors';
Expand All @@ -41,67 +16,8 @@ import { useSelectTitle } from '@app/selectors';
import type { IProps } from './types';

const Layout: FC<IProps> = ({ children }: IProps) => {
const { t } = useTranslation();
const location: Location = useLocation();
const navigate: NavigateFunction = useNavigate();
// selectors
const title: string = useSelectTitle();
// hooks
const buttonHoverBackgroundColor: string = useButtonHoverBackgroundColor();
const defaultTextColor: string = useDefaultTextColor();
// handlers
const handlerBackClick = () =>
navigate(SEARCH_ROUTE, {
replace: true,
});
// renders
const renderHeader = () => {
if (location.pathname.includes(SEARCH_ROUTE)) {
return (
<VStack
alignItems="center"
pb={DEFAULT_GAP}
pt={DEFAULT_GAP * 2}
spacing={DEFAULT_GAP}
w="full"
>
{/*icon*/}
<AVMIcon h={20} w={20} />

{/*heading*/}
<Heading
color={defaultTextColor}
size="lg"
textAlign="center"
w="full"
>
{t('headings.avmAssetList')}
</Heading>

{/*description*/}
<Text color={defaultTextColor} size="md" textAlign="center" w="full">
{t('captions.description')}
</Text>
</VStack>
);
}

return (
<HStack alignItems="flex-start" p={DEFAULT_GAP / 2} w="full">
<Tooltip label={t('captions.goBackToSearch')}>
<IconButton
_hover={{ bg: buttonHoverBackgroundColor }}
aria-label="Go back"
color={defaultTextColor}
icon={<IoArrowBackOutline />}
onClick={handlerBackClick}
size="lg"
variant="ghost"
/>
</Tooltip>
</HStack>
);
};

return (
<>
Expand All @@ -113,7 +29,7 @@ const Layout: FC<IProps> = ({ children }: IProps) => {
<Flex alignItems="center" justifyContent="center" minH="100vh" w="full">
<VStack alignItems="center" minH="100vh" spacing={0} w="full">
{/*header*/}
{renderHeader()}
<Header />

{/*content*/}
<VStack
Expand Down
20 changes: 11 additions & 9 deletions src/pages/AssetPage/AssetPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import type { IAppThunkDispatch, IAsset, ILogger } from '@app/types';
// utils
import createAssetList from '@app/utils/createAssetList';
import convertAssetToAssetAddURI from '@app/utils/convertAssetToAssetAddURI';
import { DEFAULT_GAP } from '@app/constants';

const AssetPage: FC = () => {
const { t } = useTranslation();
Expand Down Expand Up @@ -172,7 +173,8 @@ const AssetPage: FC = () => {
alignItems="center"
justifyContent="flex-start"
flexGrow={1}
spacing={4}
pb={DEFAULT_GAP}
spacing={DEFAULT_GAP}
w="full"
>
{/*icon*/}
Expand All @@ -189,16 +191,16 @@ const AssetPage: FC = () => {
{asset.name}
</Heading>

{/*information heading*/}
<PageSubHeading text={t('headings.information')} />

{/*information*/}
<VStack
alignItems="flex-start"
justifyContent="flex-start"
spacing={0}
w="full"
>
{/*heading*/}
<PageSubHeading text={t('headings.information')} />

{/*symbol*/}
<PageTextItem label={t('labels.symbol')} value={asset.symbol} />

Expand All @@ -218,16 +220,16 @@ const AssetPage: FC = () => {
/>
</VStack>

{/*actions*/}
{/*add to wallet heading*/}
<PageSubHeading text={t('headings.addToWallet')} />

{/*add to wallet*/}
<VStack
alignItems="center"
justifyContent="flex-start"
spacing={2}
spacing={DEFAULT_GAP}
w="full"
>
{/*heading*/}
<PageSubHeading text={t('headings.addToWallet')} />

{/*description*/}
<Text
color={defaultTextColor}
Expand Down
27 changes: 25 additions & 2 deletions src/pages/SearchPage/SearchPage.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,38 @@
import { VStack } from '@chakra-ui/react';
import { Heading, Text, VStack } from '@chakra-ui/react';
import {
decode as decodeBase64,
encodeURLSafe as encodeBase64URLSafe,
} from '@stablelib/base64';
import React, { FC, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import { useDispatch } from 'react-redux';
import { NavigateFunction, useNavigate } from 'react-router-dom';

// components
import AssetSelect from '@app/components/AssetSelect';
import AVMIcon from '@app/components/AVMIcon';

// constants
import { ASSET_ROUTE, CHAIN_ROUTE, DEFAULT_GAP } from '@app/constants';

// features
import { setTitle } from '@app/features/system';

// hooks
import useDefaultTextColor from '@app/hooks/useDefaultTextColor';

// types
import type { IAppThunkDispatch, IAsset } from '@app/types';

// utils
import createAssetList from '@app/utils/createAssetList';

const SearchPage: FC = () => {
const { t } = useTranslation();
const dispatch: IAppThunkDispatch = useDispatch<IAppThunkDispatch>();
const navigate: NavigateFunction = useNavigate();
// hooks
const defaultTextColor: string = useDefaultTextColor();
// misc
const assets: IAsset[] = createAssetList();
// handlers
Expand All @@ -42,9 +50,24 @@ const SearchPage: FC = () => {
alignItems="center"
justifyContent="flex-start"
flexGrow={1}
mt={DEFAULT_GAP}
spacing={DEFAULT_GAP * 2}
w="full"
>
<VStack alignItems="center" spacing={DEFAULT_GAP} w="full">
{/*icon*/}
<AVMIcon h={20} w={20} />

{/*heading*/}
<Heading color={defaultTextColor} size="lg" textAlign="center" w="full">
{t('headings.avmAssetList')}
</Heading>

{/*description*/}
<Text color={defaultTextColor} size="md" textAlign="center" w="full">
{t('captions.description')}
</Text>
</VStack>

{/*asset select*/}
<AssetSelect
assets={assets}
Expand Down
1 change: 1 addition & 0 deletions src/translations/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const translation: IResourceLanguage = {
scanQRCodeToAddToWallet:
'Scan the QR code with a compatible wallet to add the asset.',
license: 'Licensed under <2>MIT</2>.',
switchColorMode: 'Switch to {{colorMode}} mode',
},
headings: {
addToWallet: 'Add To Wallet',
Expand Down

0 comments on commit d9be770

Please sign in to comment.