Setup dark and light theme in trading app
This commit is contained in:
parent
e8a795461d
commit
ebbd50edf3
@ -1,13 +1,14 @@
|
||||
export function Vega({ className }: { className?: string }) {
|
||||
return (
|
||||
<svg className="w-[76px] h-[16px]" viewBox="0 0 283.5 61.3">
|
||||
<path d="M26.6,53.1L44,0h8.8L31.2,61.3h-9.5L0,0h8.9L26.6,53.1z" />
|
||||
<path d="M89.6,33.3v21.1h34.3v6.9H81.3V0h41.7V7H89.6v19.3h29.8v6.9H89.6z" />
|
||||
<path
|
||||
d="M156.8,7.5h7.4V54h-7.4V7.5z M193.7,0v7.5h-29.5V0H193.7z M164.2,61.3V54h29.5v7.4H164.2z M201.1,54h-7.4V39.2H179v-7.4
|
||||
h22.1V54z M201.1,7.5v7.4h-7.4V7.5H201.1z"
|
||||
/>
|
||||
<path d="M283.5,61.3h-8.6L270.4,49h-30.8L235,61.3h-8.4L250.4,0h9.3L283.5,61.3z M254.8,7.4L242.2,42h25.6L255,7.4H254.8z" />
|
||||
<svg
|
||||
width="86"
|
||||
height="19"
|
||||
viewBox="0 0 86 19"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={className}
|
||||
>
|
||||
<path d="M8.05624 16.4619L13.3478 0.0158333H16.0045L9.45138 18.9881H6.58031L0 0.0158333H2.70328L8.05624 16.4619ZM27.1835 10.3067V16.8372H37.5787V18.9873H24.6884V0.0150417H37.3503V2.16442H27.1835V8.16288H36.2395V10.3067H27.1835ZM47.5793 2.31167H49.8165V16.716H47.5793V2.31167V2.31167ZM58.7676 0V2.31167H49.8157V0H58.7683H58.7676ZM49.8157 19V16.716H58.7683V19H49.8157ZM61.0055 16.716H58.7683V12.1481H54.2924V9.86021H61.0055V16.716ZM61.0055 2.31167V4.59483H58.7683V2.31167H61.0055V2.31167ZM86 18.9873H83.3946L82.0251 15.1668H72.6801L71.3106 18.9873H68.7635L75.9769 0.0150417H78.7936L86 18.9873V18.9873ZM77.3138 2.299L73.4694 13.0213H81.239L77.3674 2.29821H77.313L77.3138 2.299Z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
@ -1,21 +1,18 @@
|
||||
import { useRouter } from 'next/router';
|
||||
import classNames from 'classnames';
|
||||
import { Vega } from '../icons/vega';
|
||||
import Link from 'next/link';
|
||||
import { AnchorButton } from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
export const Navbar = () => {
|
||||
const navClasses = classNames(
|
||||
'flex items-center',
|
||||
'border-neutral-200 border-b'
|
||||
);
|
||||
return (
|
||||
<nav className={navClasses}>
|
||||
<nav className="flex items-center">
|
||||
<Link href="/" passHref={true}>
|
||||
<a className="px-8">
|
||||
<Vega />
|
||||
<a className="px-[26px]">
|
||||
<Vega className="fill-black dark:fill-white" />
|
||||
</a>
|
||||
</Link>
|
||||
{[
|
||||
{ name: 'Trading', path: '/', exact: true },
|
||||
{ name: 'Portfolio', path: '/portfolio' },
|
||||
{ name: 'Markets', path: '/markets' },
|
||||
].map((route) => (
|
||||
@ -28,18 +25,17 @@ export const Navbar = () => {
|
||||
interface NavLinkProps {
|
||||
name: string;
|
||||
path: string;
|
||||
exact?: boolean;
|
||||
}
|
||||
|
||||
const NavLink = ({ name, path }: NavLinkProps) => {
|
||||
const NavLink = ({ name, path, exact }: NavLinkProps) => {
|
||||
const router = useRouter();
|
||||
const className = classNames('inline-block', 'p-8', {
|
||||
// Handle direct math and child page matches
|
||||
'text-vega-pink': router.asPath === path || router.asPath.startsWith(path),
|
||||
});
|
||||
|
||||
const isActive =
|
||||
router.asPath === path || (!exact && router.asPath.startsWith(path));
|
||||
return (
|
||||
<a
|
||||
className={className}
|
||||
<AnchorButton
|
||||
variant={isActive ? 'accent' : 'inline'}
|
||||
className="px-16 h-[38px] text-h4 uppercase border-0"
|
||||
href={path}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
@ -47,6 +43,6 @@ const NavLink = ({ name, path }: NavLinkProps) => {
|
||||
}}
|
||||
>
|
||||
{name}
|
||||
</a>
|
||||
</AnchorButton>
|
||||
);
|
||||
};
|
||||
|
@ -1,20 +1,45 @@
|
||||
import { ApolloProvider } from '@apollo/client';
|
||||
import { AppProps } from 'next/app';
|
||||
import Head from 'next/head';
|
||||
import { useMemo } from 'react';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { Navbar } from '../components/navbar';
|
||||
import { createClient } from '../lib/apollo-client';
|
||||
import { ThemeSwitcher } from '@vegaprotocol/ui-toolkit';
|
||||
import './styles.css';
|
||||
|
||||
function VegaTradingApp({ Component, pageProps }: AppProps) {
|
||||
const client = useMemo(() => createClient(process.env['NX_VEGA_URL']), []);
|
||||
useCallback(() => {
|
||||
if (
|
||||
localStorage.theme === 'dark' ||
|
||||
(!('theme' in localStorage) &&
|
||||
window.matchMedia('(prefers-color-scheme: dark)').matches)
|
||||
) {
|
||||
document.documentElement.classList.add('dark');
|
||||
} else {
|
||||
document.documentElement.classList.remove('dark');
|
||||
}
|
||||
}, []);
|
||||
const setTheme = () => {
|
||||
localStorage.theme = document.documentElement.classList.toggle('dark')
|
||||
? 'dark'
|
||||
: undefined;
|
||||
};
|
||||
return (
|
||||
<ApolloProvider client={client}>
|
||||
<Head>
|
||||
<title>Welcome to trading!</title>
|
||||
<link
|
||||
rel="icon"
|
||||
href="https://vega.xyz/favicon-32x32.png"
|
||||
type="image/png"
|
||||
/>
|
||||
</Head>
|
||||
<div className="h-full grid grid-rows-[min-content,_1fr] dark:bg-black dark:text-white-60 bg-white text-black-60">
|
||||
<Navbar />
|
||||
<div className="h-full dark:bg-black dark:text-white-60 bg-white text-black-60">
|
||||
<div className="flex items-center border-b-[7px] border-vega-yellow">
|
||||
<Navbar />
|
||||
<ThemeSwitcher onToggle={setTheme} className="ml-auto mr-8 -my-2" />
|
||||
</div>
|
||||
<main>
|
||||
<Component {...pageProps} />
|
||||
</main>
|
||||
|
@ -1,6 +1,4 @@
|
||||
import { EtherscanLink } from '@vegaprotocol/ui-toolkit';
|
||||
import { Callout } from '@vegaprotocol/ui-toolkit';
|
||||
import { ReactHelpers } from '@vegaprotocol/react-helpers';
|
||||
import { Callout, Button } from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
export function Index() {
|
||||
/*
|
||||
@ -9,12 +7,20 @@ export function Index() {
|
||||
* Note: The corresponding styles are in the ./index.scss file.
|
||||
*/
|
||||
return (
|
||||
<div>
|
||||
<Callout title="Hello there" headingLevel={1}>
|
||||
Welcome trading 👋
|
||||
<div className="m-24 ">
|
||||
<Callout
|
||||
intent="help"
|
||||
title="This is what this thing does"
|
||||
iconName="endorsed"
|
||||
headingLevel={1}
|
||||
>
|
||||
<div className="flex flex-col">
|
||||
<div>With a longer explaination</div>
|
||||
<Button className="block mt-8" variant="secondary">
|
||||
Action
|
||||
</Button>
|
||||
</div>
|
||||
</Callout>
|
||||
<EtherscanLink chainId={null} address="address" />
|
||||
<ReactHelpers />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -2,8 +2,9 @@ import { AnchorHTMLAttributes, ButtonHTMLAttributes, forwardRef } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { Icon, IconName } from '../icon';
|
||||
import {
|
||||
paddingLeftProvided,
|
||||
paddingRightProvided,
|
||||
includesLeftPadding,
|
||||
includesRightPadding,
|
||||
includesBorderWidth,
|
||||
} from '../../utils/class-names';
|
||||
|
||||
interface CommonProps {
|
||||
@ -25,6 +26,9 @@ const getClassName = (
|
||||
className: CommonProps['className'],
|
||||
variant: CommonProps['variant']
|
||||
) => {
|
||||
const paddingLeftProvided = includesLeftPadding(className);
|
||||
const paddingRightProvided = includesRightPadding(className);
|
||||
const borderWidthProvided = includesBorderWidth(className);
|
||||
return classNames(
|
||||
[
|
||||
'inline-flex',
|
||||
@ -32,7 +36,6 @@ const getClassName = (
|
||||
'justify-center',
|
||||
'box-border',
|
||||
'h-28',
|
||||
'border',
|
||||
'text-ui',
|
||||
'no-underline',
|
||||
'hover:underline',
|
||||
@ -40,8 +43,10 @@ const getClassName = (
|
||||
'transition-all',
|
||||
],
|
||||
{
|
||||
'pl-28': !paddingLeftProvided(className) || variant === 'inline',
|
||||
'pr-28': !paddingRightProvided(className) || variant === 'inline',
|
||||
'pl-28': !(paddingLeftProvided || variant === 'inline'),
|
||||
'pr-28': !(paddingRightProvided || variant === 'inline'),
|
||||
|
||||
border: !borderWidthProvided,
|
||||
|
||||
'hover:border-black dark:hover:border-white': variant !== 'inline',
|
||||
'active:border-black dark:active:border-white': true,
|
||||
@ -70,11 +75,12 @@ const getClassName = (
|
||||
variant === 'accent' || variant === 'inline',
|
||||
'hover:bg-vega-yellow-dark dark:hover:bg-vega-yellow/30':
|
||||
variant === 'accent',
|
||||
'text-black dark:text-black': variant === 'accent',
|
||||
'hover:text-white dark:hover:text-white': variant === 'accent',
|
||||
|
||||
'pl-4': variant === 'inline',
|
||||
'pr-4': variant === 'inline',
|
||||
'border-0': variant === 'inline',
|
||||
'pl-4': variant === 'inline' && !paddingLeftProvided,
|
||||
'pr-4': variant === 'inline' && !paddingRightProvided,
|
||||
'border-0': variant === 'inline' && !borderWidthProvided,
|
||||
underline: variant === 'inline',
|
||||
'hover:no-underline': variant === 'inline',
|
||||
'hover:border-transparent dark:hover:border-transparent':
|
||||
|
@ -2,8 +2,8 @@ import { InputHTMLAttributes, forwardRef } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { Icon, IconName } from '../icon';
|
||||
import {
|
||||
paddingLeftProvided,
|
||||
paddingRightProvided,
|
||||
includesLeftPadding,
|
||||
includesRightPadding,
|
||||
} from '../../utils/class-names';
|
||||
|
||||
interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
|
||||
@ -36,8 +36,8 @@ export const inputClassNames = ({
|
||||
'disabled:bg-black-10 disabled:dark:bg-white-10',
|
||||
],
|
||||
{
|
||||
'pl-8': !paddingLeftProvided(className),
|
||||
'pr-8': !paddingRightProvided(className),
|
||||
'pl-8': !includesLeftPadding(className),
|
||||
'pr-8': !includesRightPadding(className),
|
||||
'border-vega-pink dark:border-vega-pink': hasError,
|
||||
},
|
||||
className
|
||||
|
@ -1,41 +1,32 @@
|
||||
export const ThemeSwitcher = ({ onToggle }: { onToggle: () => void }) => (
|
||||
<button type="button" onClick={() => onToggle()}>
|
||||
<span className="dark:hidden">
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
className="w-24 h-24"
|
||||
>
|
||||
<path
|
||||
d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z"
|
||||
className="fill-blue/60 stroke-blue-500"
|
||||
></path>
|
||||
<path
|
||||
d="M12 4v1M17.66 6.344l-.828.828M20.005 12.004h-1M17.66 17.664l-.828-.828M12 20.01V19M6.34 17.664l.835-.836M3.995 12.004h1.01M6 6l.835.836"
|
||||
className="stroke-blue"
|
||||
></path>
|
||||
export const ThemeSwitcher = ({
|
||||
onToggle,
|
||||
className,
|
||||
}: {
|
||||
onToggle: () => void;
|
||||
className?: string;
|
||||
}) => (
|
||||
<button type="button" onClick={() => onToggle()} className={className}>
|
||||
<span className="dark:hidden text-black">
|
||||
<svg viewBox="0 0 45 45" className="w-32 h-32">
|
||||
<g>
|
||||
<path
|
||||
d="M22.5 27.79a5.29 5.29 0 1 0 0-10.58 5.29 5.29 0 0 0 0 10.58Z"
|
||||
fill="currentColor"
|
||||
></path>
|
||||
<path
|
||||
d="M15.01 22.5H10M35 22.5h-5.01M22.5 29.99V35M22.5 10v5.01M17.21 27.79l-3.55 3.55M31.34 13.66l-3.55 3.55M27.79 27.79l3.55 3.55M13.66 13.66l3.55 3.55"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.3"
|
||||
strokeMiterlimit="10"
|
||||
></path>
|
||||
</g>
|
||||
</svg>
|
||||
</span>
|
||||
<span className="hidden dark:inline">
|
||||
<svg viewBox="0 0 24 24" fill="none" className="w-24 h-24">
|
||||
<span className="hidden dark:inline text-white">
|
||||
<svg viewBox="0 0 45 45" className="w-32 h-32">
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M17.715 15.15A6.5 6.5 0 0 1 9 6.035C6.106 6.922 4 9.645 4 12.867c0 3.94 3.153 7.136 7.042 7.136 3.101 0 5.734-2.032 6.673-4.853Z"
|
||||
className="fill-blue/20"
|
||||
></path>
|
||||
<path
|
||||
d="m17.715 15.15.95.316a1 1 0 0 0-1.445-1.185l.495.869ZM9 6.035l.846.534a1 1 0 0 0-1.14-1.49L9 6.035Zm8.221 8.246a5.47 5.47 0 0 1-2.72.718v2a7.47 7.47 0 0 0 3.71-.98l-.99-1.738Zm-2.72.718A5.5 5.5 0 0 1 9 9.5H7a7.5 7.5 0 0 0 7.5 7.5v-2ZM9 9.5c0-1.079.31-2.082.845-2.93L8.153 5.5A7.47 7.47 0 0 0 7 9.5h2Zm-4 3.368C5 10.089 6.815 7.75 9.292 6.99L8.706 5.08C5.397 6.094 3 9.201 3 12.867h2Zm6.042 6.136C7.718 19.003 5 16.268 5 12.867H3c0 4.48 3.588 8.136 8.042 8.136v-2Zm5.725-4.17c-.81 2.433-3.074 4.17-5.725 4.17v2c3.552 0 6.553-2.327 7.622-5.537l-1.897-.632Z"
|
||||
className="fill-blue"
|
||||
></path>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M17 3a1 1 0 0 1 1 1 2 2 0 0 0 2 2 1 1 0 1 1 0 2 2 2 0 0 0-2 2 1 1 0 1 1-2 0 2 2 0 0 0-2-2 1 1 0 1 1 0-2 2 2 0 0 0 2-2 1 1 0 0 1 1-1Z"
|
||||
className="fill-blue"
|
||||
d="M28.75 11.69A12.39 12.39 0 0 0 22.5 10a12.5 12.5 0 1 0 0 25c2.196 0 4.353-.583 6.25-1.69A12.46 12.46 0 0 0 35 22.5a12.46 12.46 0 0 0-6.25-10.81Zm-6.25 22a11.21 11.21 0 0 1-11.2-11.2 11.21 11.21 0 0 1 11.2-11.2c1.246 0 2.484.209 3.66.62a13.861 13.861 0 0 0-5 10.58 13.861 13.861 0 0 0 5 10.58 11.078 11.078 0 0 1-3.66.63v-.01Z"
|
||||
fill="currentColor"
|
||||
></path>
|
||||
</svg>
|
||||
</span>
|
||||
|
@ -1,6 +1,6 @@
|
||||
import * as EthereumUtils from './utils/web3';
|
||||
|
||||
export { Button } from './components/button';
|
||||
export { Button, AnchorButton } from './components/button';
|
||||
export { Callout } from './components/callout';
|
||||
export { EthereumUtils };
|
||||
export { EtherscanLink } from './components/etherscan-link';
|
||||
|
@ -1,39 +1,39 @@
|
||||
import { paddingLeftProvided, paddingRightProvided } from './class-names';
|
||||
import { includesLeftPadding, includesRightPadding } from './class-names';
|
||||
|
||||
test('paddingLeftProvided detects class name which affects left padding', () => {
|
||||
expect(paddingLeftProvided()).toEqual(false);
|
||||
expect(paddingLeftProvided('')).toEqual(false);
|
||||
expect(paddingLeftProvided('pl-8')).toEqual(true);
|
||||
expect(paddingLeftProvided('pl-16')).toEqual(true);
|
||||
expect(paddingLeftProvided(' pl-16')).toEqual(true);
|
||||
expect(paddingLeftProvided('prepend pl-8')).toEqual(true);
|
||||
expect(paddingLeftProvided('pl-16 ')).toEqual(true);
|
||||
expect(paddingLeftProvided('pl-16 append')).toEqual(true);
|
||||
expect(paddingLeftProvided('px-8')).toEqual(true);
|
||||
expect(paddingLeftProvided('px-16')).toEqual(true);
|
||||
expect(paddingLeftProvided(' px-16')).toEqual(true);
|
||||
expect(paddingLeftProvided('prepend px-8')).toEqual(true);
|
||||
expect(paddingLeftProvided('px-16 ')).toEqual(true);
|
||||
expect(paddingLeftProvided('px-16 append')).toEqual(true);
|
||||
expect(paddingLeftProvided('px-16a')).toEqual(false);
|
||||
expect(paddingLeftProvided('apx-16')).toEqual(false);
|
||||
test('includesLeftPadding detects class name which affects left padding', () => {
|
||||
expect(includesLeftPadding()).toEqual(false);
|
||||
expect(includesLeftPadding('')).toEqual(false);
|
||||
expect(includesLeftPadding('pl-8')).toEqual(true);
|
||||
expect(includesLeftPadding('pl-16')).toEqual(true);
|
||||
expect(includesLeftPadding(' pl-16')).toEqual(true);
|
||||
expect(includesLeftPadding('prepend pl-8')).toEqual(true);
|
||||
expect(includesLeftPadding('pl-16 ')).toEqual(true);
|
||||
expect(includesLeftPadding('pl-16 append')).toEqual(true);
|
||||
expect(includesLeftPadding('px-8')).toEqual(true);
|
||||
expect(includesLeftPadding('px-16')).toEqual(true);
|
||||
expect(includesLeftPadding(' px-16')).toEqual(true);
|
||||
expect(includesLeftPadding('prepend px-8')).toEqual(true);
|
||||
expect(includesLeftPadding('px-16 ')).toEqual(true);
|
||||
expect(includesLeftPadding('px-16 append')).toEqual(true);
|
||||
expect(includesLeftPadding('px-16a')).toEqual(false);
|
||||
expect(includesLeftPadding('apx-16')).toEqual(false);
|
||||
});
|
||||
|
||||
test('paddingRightProvided detects class name which affects right padding', () => {
|
||||
expect(paddingRightProvided()).toEqual(false);
|
||||
expect(paddingRightProvided('')).toEqual(false);
|
||||
expect(paddingRightProvided('pr-8')).toEqual(true);
|
||||
expect(paddingRightProvided('pr-16')).toEqual(true);
|
||||
expect(paddingRightProvided(' pr-16')).toEqual(true);
|
||||
expect(paddingRightProvided('prepend pr-8')).toEqual(true);
|
||||
expect(paddingRightProvided('pr-16 ')).toEqual(true);
|
||||
expect(paddingRightProvided('pr-16 append')).toEqual(true);
|
||||
expect(paddingRightProvided('px-8')).toEqual(true);
|
||||
expect(paddingRightProvided('px-16')).toEqual(true);
|
||||
expect(paddingRightProvided(' px-16')).toEqual(true);
|
||||
expect(paddingRightProvided('prepend px-8')).toEqual(true);
|
||||
expect(paddingRightProvided('px-16 ')).toEqual(true);
|
||||
expect(paddingRightProvided('px-16 append')).toEqual(true);
|
||||
expect(paddingRightProvided('px-16a')).toEqual(false);
|
||||
expect(paddingRightProvided('apx-16')).toEqual(false);
|
||||
test('includesRightPadding detects class name which affects right padding', () => {
|
||||
expect(includesRightPadding()).toEqual(false);
|
||||
expect(includesRightPadding('')).toEqual(false);
|
||||
expect(includesRightPadding('pr-8')).toEqual(true);
|
||||
expect(includesRightPadding('pr-16')).toEqual(true);
|
||||
expect(includesRightPadding(' pr-16')).toEqual(true);
|
||||
expect(includesRightPadding('prepend pr-8')).toEqual(true);
|
||||
expect(includesRightPadding('pr-16 ')).toEqual(true);
|
||||
expect(includesRightPadding('pr-16 append')).toEqual(true);
|
||||
expect(includesRightPadding('px-8')).toEqual(true);
|
||||
expect(includesRightPadding('px-16')).toEqual(true);
|
||||
expect(includesRightPadding(' px-16')).toEqual(true);
|
||||
expect(includesRightPadding('prepend px-8')).toEqual(true);
|
||||
expect(includesRightPadding('px-16 ')).toEqual(true);
|
||||
expect(includesRightPadding('px-16 append')).toEqual(true);
|
||||
expect(includesRightPadding('px-16a')).toEqual(false);
|
||||
expect(includesRightPadding('apx-16')).toEqual(false);
|
||||
});
|
||||
|
@ -1,4 +1,8 @@
|
||||
export const paddingLeftProvided = (className?: string) =>
|
||||
export const includesLeftPadding = (className?: string) =>
|
||||
!!className?.match(/(^| )p(l|x)-\d+( |$)/);
|
||||
export const paddingRightProvided = (className?: string) =>
|
||||
|
||||
export const includesRightPadding = (className?: string) =>
|
||||
!!className?.match(/(^| )p(r|x)-\d+( |$)/);
|
||||
|
||||
export const includesBorderWidth = (className?: string) =>
|
||||
!!className?.match(/(^| )border-\d+( |$)/);
|
||||
|
Loading…
Reference in New Issue
Block a user