diff --git a/public/chart-bars-background.svg b/public/chart-bars-background.svg
new file mode 100644
index 0000000..a8df022
--- /dev/null
+++ b/public/chart-bars-background.svg
@@ -0,0 +1,19 @@
+
diff --git a/public/chart-bars.svg b/public/chart-bars.svg
new file mode 100644
index 0000000..9a2e431
--- /dev/null
+++ b/public/chart-bars.svg
@@ -0,0 +1,20 @@
+
diff --git a/src/components/Panel.stories.tsx b/src/components/Panel.stories.tsx
index ecdb993..1ef7d85 100644
--- a/src/components/Panel.stories.tsx
+++ b/src/components/Panel.stories.tsx
@@ -1,10 +1,10 @@
import type { Story } from '@ladle/react';
-import { Panel } from '@/components/Panel';
+import { Panel, PanelProps } from '@/components/Panel';
import { StoryWrapper } from '.ladle/components';
-export const PanelStory: Story<{ slotHeader: React.ReactNode, children?: React.ReactNode }> = (args) => {
+export const PanelStory: Story = (args) => {
return (
@@ -13,6 +13,8 @@ export const PanelStory: Story<{ slotHeader: React.ReactNode, children?: React.R
};
PanelStory.args = {
- slotHeader: 'Header',
+ slotHeaderContent: 'Header',
children: 'Content',
+ slotRight: '1️⃣',
+ hasSeparator: true,
};
diff --git a/src/components/Panel.tsx b/src/components/Panel.tsx
index cebd1d7..1ea335f 100644
--- a/src/components/Panel.tsx
+++ b/src/components/Panel.tsx
@@ -6,7 +6,7 @@ import { Icon, IconName } from '@/components/Icon';
import { layoutMixins } from '@/styles/layoutMixins';
import { breakpoints } from '@/styles';
-type PanelProps = {
+type ElementProps = {
slotHeaderContent?: React.ReactNode;
slotHeader?: React.ReactNode;
slotRight?: React.ReactNode;
@@ -16,11 +16,13 @@ type PanelProps = {
onClick?: () => void;
};
-type PanelStyleProps = {
+type StyleProps = {
className?: string;
hasSeparator?: boolean;
};
+export type PanelProps = ElementProps & StyleProps;
+
export const Panel = ({
slotHeaderContent,
slotHeader,
@@ -31,7 +33,7 @@ export const Panel = ({
onClick,
hasSeparator,
className,
-}: PanelProps & PanelStyleProps) => (
+}: PanelProps) => (
{href ? (
diff --git a/src/constants/dialogs.ts b/src/constants/dialogs.ts
index e5cf8fd..bad393c 100644
--- a/src/constants/dialogs.ts
+++ b/src/constants/dialogs.ts
@@ -2,6 +2,7 @@ export enum DialogTypes {
ClosePosition = 'ClosePosition',
Deposit = 'Deposit',
DisconnectWallet = 'DisconnectWallet',
+ DisplaySettings = 'DisplaySettings',
ExchangeOffline = 'ExchangeOffline',
ExternalLink = 'ExternalLink',
FillDetails = 'FillDetails',
diff --git a/src/layout/DialogManager.tsx b/src/layout/DialogManager.tsx
index d7145ae..04406cc 100644
--- a/src/layout/DialogManager.tsx
+++ b/src/layout/DialogManager.tsx
@@ -9,6 +9,7 @@ import { getActiveDialog } from '@/state/dialogsSelectors';
import { ClosePositionDialog } from '@/views/dialogs/ClosePositionDialog';
import { DepositDialog } from '@/views/dialogs/DepositDialog';
import { DisconnectDialog } from '@/views/dialogs/DisconnectDialog';
+import { DisplaySettingsDialog } from '@/views/dialogs/DisplaySettingsDialog';
import { ExchangeOfflineDialog } from '@/views/dialogs/ExchangeOfflineDialog';
import { HelpDialog } from '@/views/dialogs/HelpDialog';
import { ExternalLinkDialog } from '@/views/dialogs/ExternalLinkDialog';
@@ -51,6 +52,7 @@ export const DialogManager = () => {
return {
[DialogTypes.ClosePosition]: ,
[DialogTypes.Deposit]: ,
+ [DialogTypes.DisplaySettings]: ,
[DialogTypes.DisconnectWallet]: ,
[DialogTypes.ExchangeOffline]: ,
[DialogTypes.FillDetails]: ,
diff --git a/src/views/dialogs/DisplaySettingsDialog.tsx b/src/views/dialogs/DisplaySettingsDialog.tsx
new file mode 100644
index 0000000..67fdc83
--- /dev/null
+++ b/src/views/dialogs/DisplaySettingsDialog.tsx
@@ -0,0 +1,171 @@
+import { useDispatch, useSelector } from 'react-redux';
+import styled, { AnyStyledComponent, css } from 'styled-components';
+
+import { Root, Item, Indicator } from '@radix-ui/react-radio-group';
+
+import { AppTheme, setAppTheme } from '@/state/configs';
+import { getAppTheme } from '@/state/configsSelectors';
+
+import { layoutMixins } from '@/styles/layoutMixins';
+import { Themes } from '@/styles/themes';
+
+import { STRING_KEYS } from '@/constants/localization';
+
+import { Icon, IconName } from '@/components/Icon';
+
+import { Panel } from '@/components/Panel';
+
+import {
+ useStringGetter,
+ } from '@/hooks';
+
+import { Dialog } from '@/components/Dialog';
+import { HorizontalSeparatorFiller } from '@/components/Separator';
+
+type ElementProps = {
+ setIsOpen: (open: boolean) => void;
+ };
+
+export const DisplaySettingsDialog = ({ setIsOpen }: ElementProps) => {
+ const dispatch = useDispatch();
+ const stringGetter = useStringGetter();
+
+ const currentTheme: AppTheme = useSelector(getAppTheme);
+
+ const sectionHeader = (heading: string) => {
+ return (
+
+ { heading }
+
+
+ );
+ };
+
+ const themePanels = () => {
+ return (
+
+ {[{
+ theme: AppTheme.Classic,
+ label: STRING_KEYS.CLASSIC_DARK
+ }, {
+ theme: AppTheme.Dark,
+ label: STRING_KEYS.DARK
+ }, {
+ theme: AppTheme.Light,
+ label: STRING_KEYS.LIGHT
+ }].map(({theme, label}) => (
+ -
+ {
+ dispatch(setAppTheme(theme));
+ }}
+ slotHeader={{stringGetter({ key: label })}}
+ >
+
+
+
+
+
+
+ ))}
+
+ )
+ }
+
+ return (
+
+ );
+};
+
+const Styled: Record = {};
+
+Styled.Section = styled.div`
+ display: grid;
+ gap: 1.5rem;
+`;
+
+Styled.Header = styled.header`
+ ${layoutMixins.inlineRow}
+`;
+
+Styled.Root = styled(Root)`
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 1.5rem;
+`;
+
+Styled.Panel = styled(Panel)<{ backgroundColor: string, gridColor: string }>`
+ --panel-content-paddingY: 0.25rem;
+ --panel-content-paddingX: 0.25rem;
+
+ ${({ backgroundColor, gridColor }) => css`
+ --themePanel-backgroundColor: ${backgroundColor};
+ --themePanel-gridColor: ${gridColor};
+ `}
+
+ &:before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ bottom: 0;
+ right: 0;
+
+ background: radial-gradient(55% 35% at 50% 65%, transparent, var(--themePanel-backgroundColor) 100%);
+ background-color: var(--themePanel-gridColor);
+ mask-image: url('/chart-bars-background.svg');
+ mask-size: cover;
+ }
+
+ position: relative;
+ padding: 0.75rem;
+
+ background-color: var(--themePanel-backgroundColor);
+ border: solid var(--border-width) var(--color-border);
+`;
+
+Styled.PanelHeader = styled.h3<{ textColor: string }>`
+ ${({ textColor }) => css`
+ color: ${textColor};
+ `}
+
+ align-self: flex-start;
+`;
+
+Styled.Image = styled.img`
+ width: 100%;
+ height: auto;
+`
+
+Styled.Indicator = styled(Indicator)`
+ --indicator-size: 1.25rem;
+
+ height: var(--indicator-size);
+ width: var(--indicator-size);
+
+ position: absolute;
+ bottom: 0;
+ right: 0;
+
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ background-color: var(--color-accent);
+ border-radius: 50%;
+ color: var(--color-text-2);
+`
+
+Styled.CheckIcon = styled(Icon)`
+ --icon-size: 0.625rem;
+
+ width: var(--icon-size);
+ height: var(--icon-size);
+`;
\ No newline at end of file
diff --git a/src/views/menus/AccountMenu.tsx b/src/views/menus/AccountMenu.tsx
index 2796a88..ac2ce49 100644
--- a/src/views/menus/AccountMenu.tsx
+++ b/src/views/menus/AccountMenu.tsx
@@ -32,9 +32,11 @@ import { Icon, IconName } from '@/components/Icon';
import { IconButton } from '@/components/IconButton';
import { WithTooltip } from '@/components/WithTooltip';
+import { AppTheme } from '@/state/configs';
import { openDialog } from '@/state/dialogs';
import { getOnboardingState, getSubaccount } from '@/state/accountSelectors';
+import { getAppTheme } from '@/state/configsSelectors';
import { isTruthy } from '@/lib/isTruthy';
import { truncateAddress } from '@/lib/wallet';
@@ -50,6 +52,7 @@ export const AccountMenu = () => {
const { freeCollateral } = useSelector(getSubaccount, shallowEqual) || {};
const { nativeTokenBalance } = useAccountBalance();
const { usdcLabel, chainTokenLabel } = useTokenConfigs();
+ const theme = useSelector(getAppTheme);
const { evmAddress, walletType, dydxAddress, hdKey } = useAccounts();
@@ -177,6 +180,12 @@ export const AccountMenu = () => {
label: stringGetter({ key: STRING_KEYS.PREFERENCES }),
onSelect: () => dispatch(openDialog({ type: DialogTypes.Preferences })),
},
+ {
+ value: 'Display Settings',
+ icon: theme === AppTheme.Light ? : ,
+ label: stringGetter({ key: STRING_KEYS.DISPLAY_SETTINGS }),
+ onSelect: () => dispatch(openDialog({ type: DialogTypes.DisplaySettings })),
+ },
...(onboardingState === OnboardingState.AccountConnected && hdKey
? [
(!isMainnet || testFlags.showMobileSignInOption) && {