Compare commits

...

2 Commits

Author SHA1 Message Date
moo-onthelawn
dd941a543e
TRCL-3535 Add green/red preference to display modal (#261)
* add functionality, without user exposure yet

* fix commit history

* remove unnecessary change

* lint

* update css

* audit of pos/neg colors

* fix flipping of disconnect/export keys colors

* review comments

* clean up naming of icon

* fix icon sizing

* small edit to story example
2024-02-01 19:22:42 -05:00
mulan xia
eacab6ae04
add functionality, without user exposure yet 2024-02-01 19:22:42 -05:00
35 changed files with 333 additions and 107 deletions

View File

@ -9,9 +9,9 @@ import { GlobalStyle } from '@/styles/globalStyle';
import { SelectMenu, SelectItem } from '@/components/SelectMenu';
import { AppThemeProvider } from '@/hooks/useAppTheme';
import { AppThemeAndColorModeProvider } from '@/hooks/useAppThemeAndColorMode';
import { AppTheme, setAppTheme } from '@/state/configs';
import { AppTheme, AppColorMode, setAppTheme, setAppColorMode } from '@/state/configs';
import { setLocaleLoaded } from '@/state/localization';
import '@/index.css';
@ -19,9 +19,12 @@ import './ladle.css';
export const StoryWrapper: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const [theme, setTheme] = useState(AppTheme.Classic);
const [colorMode, setColorMode] = useState(AppColorMode.GreenUp);
useEffect(() => {
store.dispatch(setAppTheme(theme));
store.dispatch(setAppColorMode(colorMode));
switch (theme) {
case AppTheme.Dark: {
document?.documentElement?.classList.remove('theme-light');
@ -38,7 +41,7 @@ export const StoryWrapper: React.FC<{ children: React.ReactNode }> = ({ children
break;
}
}
}, [theme]);
}, [theme, colorMode]);
useEffect(() => {
store.dispatch(setLocaleLoaded(true));
@ -48,10 +51,7 @@ export const StoryWrapper: React.FC<{ children: React.ReactNode }> = ({ children
<Provider store={store}>
<StoryHeader>
<h4>Active Theme:</h4>
<SelectMenu
value={theme}
onValueChange={setTheme}
>
<SelectMenu value={theme} onValueChange={setTheme}>
{[
{
value: AppTheme.Classic,
@ -66,20 +66,31 @@ export const StoryWrapper: React.FC<{ children: React.ReactNode }> = ({ children
label: 'Light theme',
},
].map(({ value, label }) => (
<SelectItem
key={value}
value={value}
label={label}
/>
<SelectItem key={value} value={value} label={label} />
))}
</SelectMenu>
<h4>Active Color Mode:</h4>
<SelectMenu value={colorMode} onValueChange={setColorMode}>
{[
{
value: AppColorMode.GreenUp,
label: 'Green up',
},
{
value: AppColorMode.RedUp,
label: 'Red up',
},
].map(({ value, label }) => (
<SelectItem key={value} value={value} label={label} />
))}
</SelectMenu>
</StoryHeader>
<hr />
<AppThemeProvider>
<AppThemeAndColorModeProvider>
<GlobalStyle />
<StoryContent>{children}</StoryContent>
</AppThemeProvider>
</Provider>
</AppThemeAndColorModeProvider>
</Provider>
);
};

View File

@ -16,7 +16,7 @@ import {
} from '@/hooks';
import { DydxProvider } from '@/hooks/useDydxClient';
import { AccountsProvider } from '@/hooks/useAccounts';
import { AppThemeProvider } from '@/hooks/useAppTheme';
import { AppThemeAndColorModeProvider } from '@/hooks/useAppThemeAndColorMode';
import { DialogAreaProvider, useDialogArea } from '@/hooks/useDialogArea';
import { LocaleProvider } from '@/hooks/useLocaleSeparators';
import { NotificationsProvider } from '@/hooks/useNotifications';
@ -141,8 +141,7 @@ const providers = [
wrapProvider(LocalNotificationsProvider),
wrapProvider(NotificationsProvider),
wrapProvider(DialogAreaProvider),
wrapProvider(PotentialMarketsProvider),
wrapProvider(AppThemeProvider),
wrapProvider(AppThemeAndColorModeProvider),
];
const App = () => {

View File

@ -102,13 +102,13 @@ const buttonActionVariants = {
[ButtonAction.Create]: css`
--button-textColor: var(--color-text-2);
--button-backgroundColor: var(--color-positive);
--button-backgroundColor: var(--color-success);
--button-border: solid var(--border-width) var(--color-border-white);
`,
[ButtonAction.Destroy]: css`
--button-textColor: var(--color-text-2);
--button-backgroundColor: var(--color-negative);
--button-backgroundColor: var(--color-error);
--button-border: solid var(--border-width) var(--color-border-white);
`,
@ -119,7 +119,7 @@ const buttonActionVariants = {
`,
[ButtonAction.Reset]: css`
--button-textColor: var(--color-negative);
--button-textColor: var(--color-error);
--button-backgroundColor: var(--color-layer-3);
--button-border: solid var(--border-width) var(--color-border-red);
`,

View File

@ -87,7 +87,7 @@ Styled.Icon = styled(Icon)<{ copied: boolean }>`
${({ copied }) =>
copied &&
css`
color: var(--color-positive);
color: var(--color-success);
`}
`;
@ -96,7 +96,7 @@ Styled.IconButton = styled(IconButton)<{ copied: boolean }>`
copied &&
css`
svg {
color: var(--color-positive);
color: var(--color-success);
}
`}
`;

View File

@ -62,6 +62,6 @@ Styled.DiffArrowContainer = styled.span<DiffArrowProps>`
`,
down: css`
transform: rotate(90deg);
`
`,
}[direction || 'right'])}
`;

View File

@ -75,7 +75,7 @@ Styled.DiffValue = styled.div<{ hasInvalidNewValue?: boolean }>`
${({ hasInvalidNewValue }) =>
hasInvalidNewValue &&
css`
color: var(--color-negative);
color: var(--color-error);
`}
`;

View File

@ -6,29 +6,34 @@ import { StoryWrapper } from '.ladle/components';
export const DropdownMenuStory: Story<Parameters<typeof DropdownMenu>> = (args) => {
const exampleItems = [
{
value: '0',
label: 'Item 0',
onSelect: () => alert('Item 0 action'),
},
{
value: '1',
label: 'Item 1',
label: 'Item 1 (accent)',
onSelect: () => alert('Item 1 action'),
highlightColor: 'accent',
},
{
value: '2',
label: 'Item 2',
label: 'Item 2 (create)',
onSelect: () => alert('Item 2 action'),
highlightColor: 'create',
},
{
value: '3',
label: 'Item 3',
label: 'Item 3 (destroy)',
onSelect: () => alert('Item 3 action'),
highlightColor: 'destroy',
},
];
return (
<StoryWrapper>
<DropdownMenu
{...args}
items={exampleItems}
>
<DropdownMenu {...args} items={exampleItems}>
<span>Menu</span>
</DropdownMenu>
</StoryWrapper>

View File

@ -13,7 +13,7 @@ export type DropdownMenuItem<T> = {
label: React.ReactNode;
onSelect?: () => void;
separator?: boolean;
highlightColor?: 'accent' | 'positive' | 'negative';
highlightColor?: 'accent' | 'create' | 'destroy';
};
type StyleProps = {
@ -82,7 +82,7 @@ Styled.Separator = styled(Separator)`
margin: 0.25rem 1rem;
`;
Styled.Item = styled(Item)<{ $highlightColor: 'accent' | 'positive' | 'negative' }>`
Styled.Item = styled(Item)<{ $highlightColor: 'accent' | 'create' | 'destroy' }>`
${popoverMixins.item}
--item-font-size: var(--dropdownMenu-item-font-size);
${({ $highlightColor }) =>
@ -90,11 +90,11 @@ Styled.Item = styled(Item)<{ $highlightColor: 'accent' | 'positive' | 'negative'
['accent']: `
--item-highlighted-textColor: var(--color-accent);
`,
['positive']: `
--item-highlighted-textColor: var(--color-positive);
['create']: `
--item-highlighted-textColor: var(--color-success);
`,
['negative']: `
--item-highlighted-textColor: var(--color-negative);
['destroy']: `
--item-highlighted-textColor: var(--color-error);
`,
}[$highlightColor])}

View File

@ -117,7 +117,7 @@ Styled.ConfirmButton = styled(Styled.IconButton)`
--button-backgroundColor: hsla(203, 25%, 19%, 1);
svg {
color: var(--color-positive);
color: var(--color-success);
}
`;
@ -125,7 +125,7 @@ Styled.CancelButton = styled(Styled.IconButton)`
--button-backgroundColor: hsla(296, 16%, 18%, 1);
svg {
color: var(--color-negative);
color: var(--color-error);
width: 0.8em;
height: 0.8em;

View File

@ -20,6 +20,7 @@ export enum LocalStorageKey {
SelectedLocale = 'dydx.SelectedLocale',
SelectedNetwork = 'dydx.SelectedNetwork',
SelectedTheme = 'dydx.SelectedTheme',
SelectedColorMode = 'dydx.SelectedColorMode',
SelectedTradeLayout = 'dydx.SelectedTradeLayout',
TradingViewChartConfig = 'dydx.TradingViewChartConfig',
HasSeenLaunchIncentives = 'dydx.HasSeenLaunchIncentives',

View File

@ -1,4 +1,11 @@
export type ThemeColors = LayerColors &
import { AppColorMode } from '@/state/configs';
export type Theme = {
[AppColorMode.GreenUp]: ThemeColorBase;
[AppColorMode.RedUp]: ThemeColorBase;
};
export type ThemeColorBase = LayerColors &
BorderColors &
TextColors &
GradientColors &
@ -47,8 +54,13 @@ type StatusColors = {
success: string;
warning: string;
error: string;
successFaded: string;
warningFaded: string;
errorFaded: string;
};
/** ##InvertDirectionalColors
* When adding colors here, make sure to update linked function to invert colors for AppColorMode. */
type DirectionalColors = {
positive: string;
negative: string;

View File

@ -12,8 +12,9 @@ import {
ORDERBOOK_WIDTH,
} from '@/constants/orderbook';
import { useAppThemeAndColorModeContext } from '@/hooks/useAppThemeAndColorMode';
import { getCurrentMarketConfig, getCurrentMarketOrderbookMap } from '@/state/perpetualsSelectors';
import { getAppTheme } from '@/state/configsSelectors';
import { MustBigNumber } from '@/lib/numbers';
@ -23,7 +24,6 @@ import {
getXByColumn,
getYForElements,
} from '@/lib/orderbookHelpers';
import { useAppThemeContext } from '../useAppTheme';
type ElementProps = {
data: Array<PerpetualMarketOrderbookLevel | undefined>;
@ -53,7 +53,7 @@ export const useDrawOrderbook = ({
const { stepSizeDecimals = TOKEN_DECIMALS, tickSizeDecimals = SMALL_USD_DECIMALS } =
useSelector(getCurrentMarketConfig, shallowEqual) || {};
const prevData = useRef<typeof data>(data);
const theme = useAppThemeContext();
const theme = useAppThemeAndColorModeContext();
/**
* Scale canvas using device pixel ratio to unblur drawn text

View File

@ -11,7 +11,7 @@ import { useDydxClient, useLocalStorage } from '@/hooks';
import { store } from '@/state/_store';
import { getSelectedNetwork } from '@/state/appSelectors';
import { getAppTheme } from '@/state/configsSelectors';
import { getAppTheme, getAppColorMode } from '@/state/configsSelectors';
import { getSelectedLocale } from '@/state/localizationSelectors';
import { getCurrentMarketId, getMarketIds } from '@/state/perpetualsSelectors';
@ -30,6 +30,7 @@ export const useTradingView = ({
}) => {
const marketId = useSelector(getCurrentMarketId);
const appTheme = useSelector(getAppTheme);
const appColorMode = useSelector(getAppColorMode);
const marketIds = useSelector(getMarketIds, shallowEqual);
const selectedLocale = useSelector(getSelectedLocale);
const selectedNetwork = useSelector(getSelectedNetwork);
@ -46,7 +47,7 @@ export const useTradingView = ({
useEffect(() => {
if (hasMarkets && isClientConnected && marketId) {
const widgetOptions = getWidgetOptions();
const widgetOverrides = getWidgetOverrides(appTheme);
const widgetOverrides = getWidgetOverrides({ appTheme, appColorMode });
const options = {
// debug: true,
...widgetOptions,
@ -75,7 +76,14 @@ export const useTradingView = ({
tvWidgetRef.current = null;
setIsChartReady(false);
};
}, [getCandlesForDatafeed, isClientConnected, hasMarkets, selectedLocale, selectedNetwork, !!marketId]);
}, [
getCandlesForDatafeed,
isClientConnected,
hasMarkets,
selectedLocale,
selectedNetwork,
!!marketId,
]);
return { savedResolution };
};

View File

@ -3,8 +3,8 @@ import { useSelector } from 'react-redux';
import type { IChartingLibraryWidget, ThemeName } from 'public/tradingview/charting_library';
import { AppTheme } from '@/state/configs';
import { getAppTheme } from '@/state/configsSelectors';
import { AppColorMode, AppTheme } from '@/state/configs';
import { getAppTheme, getAppColorMode } from '@/state/configsSelectors';
import { getWidgetOverrides } from '@/lib/tradingView/utils';
@ -29,6 +29,7 @@ export const useTradingViewTheme = ({
isWidgetReady?: boolean;
}) => {
const appTheme: AppTheme = useSelector(getAppTheme);
const appColorMode: AppColorMode = useSelector(getAppColorMode);
useEffect(() => {
if (tvWidget && isWidgetReady) {
@ -55,10 +56,24 @@ export const useTradingViewTheme = ({
}
}
const { overrides, studies_overrides } = getWidgetOverrides(appTheme);
const { overrides, studies_overrides } = getWidgetOverrides({ appTheme, appColorMode });
tvWidget?.applyOverrides(overrides);
tvWidget?.applyStudiesOverrides(studies_overrides);
// Necessary to update existing indicators
const volumeStudyId = tvWidget
?.activeChart()
?.getAllStudies()
?.find((x) => x.name === 'Volume')?.id;
if (volumeStudyId) {
const volume = tvWidget?.activeChart()?.getStudyById(volumeStudyId);
volume.applyOverrides({
'volume.color.0': studies_overrides['volume.volume.color.0'],
'volume.color.1': studies_overrides['volume.volume.color.1'],
});
}
});
}
}, [appTheme]);
}, [appTheme, appColorMode]);
};

View File

@ -1,16 +0,0 @@
import { useSelector } from 'react-redux';
import { ThemeProvider } from 'styled-components';
import { AppTheme } from '@/state/configs';
import { getAppTheme } from '@/state/configsSelectors';
import { Themes } from '@/styles/themes';
export const AppThemeProvider = ({ ...props }) => {
return <ThemeProvider theme={useAppThemeContext()} {...props} />
};
export const useAppThemeContext = () => {
const theme: AppTheme = useSelector(getAppTheme);
return Themes[theme];
}

View File

@ -0,0 +1,18 @@
import { useSelector } from 'react-redux';
import { ThemeProvider } from 'styled-components';
import { AppTheme, AppColorMode } from '@/state/configs';
import { getAppTheme, getAppColorMode } from '@/state/configsSelectors';
import { Themes } from '@/styles/themes';
export const AppThemeAndColorModeProvider = ({ ...props }) => {
return <ThemeProvider theme={useAppThemeAndColorModeContext()} {...props} />;
};
export const useAppThemeAndColorModeContext = () => {
const theme: AppTheme = useSelector(getAppTheme);
const colorMode: AppColorMode = useSelector(getAppColorMode);
return Themes[theme][colorMode];
};

View File

@ -1,7 +1,7 @@
import { useAppThemeContext } from '@/hooks/useAppTheme';
import { useAppThemeAndColorModeContext } from '@/hooks/useAppThemeAndColorMode';
const LogoShortIcon: React.FC<{ id?: string }> = ({ id }: { id?: string }) => {
const theme = useAppThemeContext();
const theme = useAppThemeAndColorModeContext();
const fill = theme.logoFill;
return (

View File

@ -127,7 +127,7 @@ Styled.StatusDot = styled.div<{ exchangeStatus: ExchangeStatus }>`
background-color: ${({ exchangeStatus }) =>
({
[ExchangeStatus.Degraded]: css`var(--color-warning)`,
[ExchangeStatus.Operational]: css`var(--color-positive)`,
[ExchangeStatus.Operational]: css`var(--color-success)`,
}[exchangeStatus])};
`;

View File

@ -43,19 +43,19 @@ export const getStatusIconInfo = ({
case AbacusOrderStatus.filled: {
return {
statusIcon: IconName.OrderFilled,
statusIconColor: `var(--color-positive)`,
statusIconColor: `var(--color-success)`,
};
}
case AbacusOrderStatus.cancelled: {
return {
statusIcon: IconName.OrderCanceled,
statusIconColor: `var(--color-negative)`,
statusIconColor: `var(--color-error)`,
};
}
case AbacusOrderStatus.canceling: {
return {
statusIcon: IconName.OrderPending,
statusIconColor: `var(--color-negative)`,
statusIconColor: `var(--color-error)`,
};
}
case AbacusOrderStatus.untriggered: {

View File

@ -1,6 +1,6 @@
import { Candle, TradingViewBar, TradingViewSymbol } from '@/constants/candles';
import { AppTheme } from '@/state/configs';
import { AppTheme, AppColorMode } from '@/state/configs';
import { Themes } from '@/styles/themes';
@ -47,8 +47,14 @@ export const getHistorySlice = ({
return bars.filter(({ time }) => time >= fromMs);
};
export const getWidgetOverrides = (appTheme: AppTheme) => {
const theme = Themes[appTheme];
export const getWidgetOverrides = ({
appTheme,
appColorMode,
}: {
appTheme: AppTheme;
appColorMode: AppColorMode;
}) => {
const theme = Themes[appTheme][appColorMode];
return {
overrides: {

View File

@ -286,10 +286,10 @@ Styled.ConnectedIcon = styled.div`
height: 0.5rem;
width: 0.5rem;
margin-right: 0.25rem;
background: var(--color-positive);
background: var(--color-success);
border-radius: 50%;
box-shadow: 0 0 0 0.2rem var(--color-gradient-positive);
box-shadow: 0 0 0 0.2rem var(--color-gradient-success);
`;
Styled.Address = styled.h1`
@ -318,7 +318,7 @@ Styled.ActionButton = styled(IconButton)<{ iconName?: IconName }>`
${({ iconName }) =>
iconName === IconName.Close
? css`
--button-textColor: var(--color-negative);
--button-textColor: var(--color-error);
--button-icon-size: 0.75em;
`
: iconName === IconName.Transfer &&

View File

@ -12,8 +12,14 @@ export enum AppTheme {
Light = 'Light',
}
export enum AppColorMode {
GreenUp = 'GreenUp',
RedUp = 'RedUp',
}
export interface ConfigsState {
appTheme: AppTheme;
appColorMode: AppColorMode;
feeTiers?: kollections.List<FeeTier>;
feeDiscounts?: FeeDiscount[];
network?: NetworkConfigs;
@ -41,6 +47,10 @@ const initialState: ConfigsState = {
key: LocalStorageKey.SelectedTheme,
defaultValue: AppTheme.Classic,
}),
appColorMode: getLocalStorage({
key: LocalStorageKey.SelectedColorMode,
defaultValue: AppColorMode.GreenUp,
}),
feeDiscounts: undefined,
feeTiers: undefined,
network: undefined,
@ -61,6 +71,10 @@ export const configsSlice = createSlice({
changeTheme(payload);
state.appTheme = payload;
},
setAppColorMode: (state: ConfigsState, { payload }: PayloadAction<AppColorMode>) => {
setLocalStorage({ key: LocalStorageKey.SelectedColorMode, value: payload });
state.appColorMode = payload;
},
setConfigs: (state: ConfigsState, action: PayloadAction<Nullable<Configs>>) => ({
...state,
...action.payload,
@ -72,4 +86,5 @@ export const configsSlice = createSlice({
},
});
export const { setAppTheme, setConfigs, markLaunchIncentivesSeen } = configsSlice.actions;
export const { setAppTheme, setAppColorMode, setConfigs, markLaunchIncentivesSeen } =
configsSlice.actions;

View File

@ -2,6 +2,8 @@ import type { RootState } from './_store';
export const getAppTheme = (state: RootState) => state.configs.appTheme;
export const getAppColorMode = (state: RootState) => state.configs.appColorMode;
export const getFeeTiers = (state: RootState) => state.configs.feeTiers?.toArray();
export const getFeeDiscounts = (state: RootState) => state.configs.feeDiscounts;

View File

@ -29,6 +29,9 @@ export const GlobalStyle = createGlobalStyle`
--color-success: ${({ theme }) => theme.success};
--color-warning: ${({ theme }) => theme.warning};
--color-error: ${({ theme }) => theme.error};
--color-gradient-success: ${({ theme }) => theme.successFaded};
--color-gradient-warning: ${({ theme }) => theme.warningFaded};
--color-gradient-error: ${({ theme }) => theme.errorFaded};
--color-positive: ${({ theme }) => theme.positive};
--color-negative: ${({ theme }) => theme.negative};

View File

@ -1,9 +1,9 @@
import { AppTheme } from '@/state/configs';
import type { ThemeColors } from '@/constants/styles/colors';
import { AppTheme, AppColorMode } from '@/state/configs';
import type { Theme, ThemeColorBase } from '@/constants/styles/colors';
import { ColorToken, OpacityToken } from '@/constants/styles/base';
import { generateFadedColorVariant } from '@/lib/styles';
const ClassicTheme: ThemeColors = {
const ClassicThemeBase: ThemeColorBase = {
layer0: ColorToken.GrayBlue7,
layer1: ColorToken.GrayBlue6,
layer2: ColorToken.GrayBlue5,
@ -31,6 +31,9 @@ const ClassicTheme: ThemeColors = {
success: ColorToken.Green1,
warning: ColorToken.Yellow0,
error: ColorToken.Red2,
successFaded: generateFadedColorVariant(ColorToken.Green1, OpacityToken.Opacity16),
warningFaded: generateFadedColorVariant(ColorToken.Yellow0, OpacityToken.Opacity16),
errorFaded: generateFadedColorVariant(ColorToken.Red2, OpacityToken.Opacity16),
positive: ColorToken.Green1,
negative: ColorToken.Red2,
@ -52,7 +55,7 @@ const ClassicTheme: ThemeColors = {
tooltipBackground: generateFadedColorVariant(ColorToken.GrayBlue3, OpacityToken.Opacity66),
};
const DarkTheme: ThemeColors = {
const DarkThemeBase: ThemeColorBase = {
layer0: ColorToken.Black,
layer1: ColorToken.DarkGray11,
layer2: ColorToken.DarkGray13,
@ -80,6 +83,9 @@ const DarkTheme: ThemeColors = {
success: ColorToken.Green0,
warning: ColorToken.Yellow0,
error: ColorToken.Red0,
successFaded: generateFadedColorVariant(ColorToken.Green0, OpacityToken.Opacity16),
warningFaded: generateFadedColorVariant(ColorToken.Yellow0, OpacityToken.Opacity16),
errorFaded: generateFadedColorVariant(ColorToken.Red0, OpacityToken.Opacity16),
positive: ColorToken.Green0,
negative: ColorToken.Red0,
@ -101,7 +107,7 @@ const DarkTheme: ThemeColors = {
tooltipBackground: generateFadedColorVariant(ColorToken.DarkGray6, OpacityToken.Opacity66),
};
const LightTheme: ThemeColors = {
const LightThemeBase: ThemeColorBase = {
layer0: ColorToken.White,
layer1: ColorToken.LightGray6,
layer2: ColorToken.White,
@ -129,6 +135,9 @@ const LightTheme: ThemeColors = {
success: ColorToken.Green2,
warning: ColorToken.Yellow0,
error: ColorToken.Red1,
successFaded: generateFadedColorVariant(ColorToken.Green2, OpacityToken.Opacity16),
warningFaded: generateFadedColorVariant(ColorToken.Yellow0, OpacityToken.Opacity16),
errorFaded: generateFadedColorVariant(ColorToken.Red1, OpacityToken.Opacity16),
positive: ColorToken.Green2,
negative: ColorToken.Red1,
@ -150,8 +159,22 @@ const LightTheme: ThemeColors = {
tooltipBackground: generateFadedColorVariant(ColorToken.LightGray7, OpacityToken.Opacity66),
};
export const Themes = {
[AppTheme.Classic]: ClassicTheme,
[AppTheme.Dark]: DarkTheme,
[AppTheme.Light]: LightTheme,
const generateTheme = (themeBase: ThemeColorBase): Theme => {
return {
[AppColorMode.GreenUp]: themeBase,
[AppColorMode.RedUp]: {
...themeBase,
// #InvertDirectionalColors
positive: themeBase.negative,
negative: themeBase.positive,
positiveFaded: themeBase.negativeFaded,
negativeFaded: themeBase.positiveFaded,
},
};
};
export const Themes = {
[AppTheme.Classic]: generateTheme(ClassicThemeBase),
[AppTheme.Dark]: generateTheme(DarkThemeBase),
[AppTheme.Light]: generateTheme(LightThemeBase),
};

View File

@ -231,7 +231,7 @@ Styled.CircleContainer = styled.div`
`;
Styled.Icon = styled(Icon)`
color: var(--color-negative);
color: var(--color-error);
`;
Styled.WithUsage = styled.div`

View File

@ -557,11 +557,11 @@ Styled.PositionTile = styled(PositionTile)``;
Styled.ClosePositionButton = styled(Button)`
--button-border: solid var(--border-width) var(--color-border-red);
--button-textColor: var(--color-negative);
--button-textColor: var(--color-error);
`;
Styled.ClosePositionToggleButton = styled(ToggleButton)`
--button-border: solid var(--border-width) var(--color-border-red);
--button-toggle-off-textColor: var(--color-negative);
--button-toggle-on-textColor: var(--color-negative);
--button-toggle-off-textColor: var(--color-error);
--button-toggle-on-textColor: var(--color-error);
`;

View File

@ -5,8 +5,8 @@ import { Root, Item, Indicator } from '@radix-ui/react-radio-group';
import { useStringGetter } from '@/hooks';
import { AppTheme, setAppTheme } from '@/state/configs';
import { getAppTheme } from '@/state/configsSelectors';
import { AppTheme, AppColorMode, setAppTheme, setAppColorMode } from '@/state/configs';
import { getAppTheme, getAppColorMode } from '@/state/configsSelectors';
import { layoutMixins } from '@/styles/layoutMixins';
import { Themes } from '@/styles/themes';
@ -26,6 +26,7 @@ export const DisplaySettingsDialog = ({ setIsOpen }: ElementProps) => {
const stringGetter = useStringGetter();
const currentTheme: AppTheme = useSelector(getAppTheme);
const currentColorMode: AppColorMode = useSelector(getAppColorMode);
const sectionHeader = (heading: string) => {
return (
@ -56,13 +57,13 @@ export const DisplaySettingsDialog = ({ setIsOpen }: ElementProps) => {
<Styled.AppThemeItem
key={theme}
value={theme}
backgroundcolor={Themes[theme].layer2}
gridcolor={Themes[theme].borderDefault}
backgroundcolor={Themes[theme][currentColorMode].layer2}
gridcolor={Themes[theme][currentColorMode].borderDefault}
onClick={() => {
dispatch(setAppTheme(theme));
}}
>
<Styled.AppThemeHeader textcolor={Themes[theme].textPrimary}>
<Styled.AppThemeHeader textcolor={Themes[theme][currentColorMode].textPrimary}>
{stringGetter({ key: label })}
</Styled.AppThemeHeader>
<Styled.Image src="/chart-bars.svg" />
@ -75,6 +76,48 @@ export const DisplaySettingsDialog = ({ setIsOpen }: ElementProps) => {
);
};
const colorModeOptions = () => {
return (
<Styled.ColorPreferenceRoot value={currentColorMode}>
{[
{
colorMode: AppColorMode.GreenUp,
label: STRING_KEYS.GREEN_IS_UP,
},
{
colorMode: AppColorMode.RedUp,
label: STRING_KEYS.RED_IS_UP,
},
].map(({ colorMode, label }) => (
<Styled.ColorPreferenceItem
key={colorMode}
value={colorMode}
onClick={() => {
dispatch(setAppColorMode(colorMode));
}}
>
<Styled.ColorPreferenceLabel>
<Styled.ArrowIconContainer>
<Styled.ArrowIcon
iconName={IconName.Arrow}
direction="up"
color={colorMode === AppColorMode.GreenUp ? 'green' : 'red'}
/>
<Styled.ArrowIcon
iconName={IconName.Arrow}
direction="down"
color={colorMode === AppColorMode.GreenUp ? 'red' : 'green'}
/>
</Styled.ArrowIconContainer>
{stringGetter({ key: label })}
</Styled.ColorPreferenceLabel>
<Styled.DotIndicator $selected={currentColorMode === colorMode} />
</Styled.ColorPreferenceItem>
))}
</Styled.ColorPreferenceRoot>
);
};
return (
<Dialog
isOpen
@ -85,6 +128,10 @@ export const DisplaySettingsDialog = ({ setIsOpen }: ElementProps) => {
{sectionHeader(stringGetter({ key: STRING_KEYS.THEME }))}
{themePanels()}
</Styled.Section>
<Styled.Section>
{sectionHeader(stringGetter({ key: STRING_KEYS.DIRECTION_COLOR_PREFERENCE }))}
{colorModeOptions()}
</Styled.Section>
</Dialog>
);
};
@ -110,6 +157,11 @@ Styled.AppThemeRoot = styled(Root)`
grid-template-columns: 1fr 1fr;
`;
Styled.ColorPreferenceRoot = styled(Root)`
${gridStyle}
grid-template-columns: 1fr;
`;
Styled.Item = styled(Item)`
--border-color: var(--color-border);
--item-padding: 0.75rem;
@ -120,9 +172,19 @@ Styled.Item = styled(Item)`
border: solid var(--border-width) var(--border-color);
border-radius: 0.875rem;
padding: var(--item-padding);
`;
Styled.ColorPreferenceItem = styled(Styled.Item)`
&[data-state='checked'] {
background-color: var(--color-layer-4);
}
${layoutMixins.row}
justify-content: space-between;
`;
Styled.AppThemeItem = styled(Styled.Item)<{ backgroundcolor: string; gridcolor: string }>`
${({ backgroundcolor, gridcolor }) => css`
--themePanel-backgroundColor: ${backgroundcolor};
@ -133,6 +195,7 @@ Styled.AppThemeItem = styled(Styled.Item)<{ backgroundcolor: string; gridcolor:
flex-direction: column;
position: relative;
width: 100%;
background-color: var(--themePanel-backgroundColor);
&::before {
@ -169,6 +232,43 @@ Styled.Image = styled.img`
z-index: 1;
`;
Styled.ColorPreferenceLabel = styled.div`
${layoutMixins.inlineRow};
gap: 1ch;
`;
Styled.ArrowIconContainer = styled.div`
${layoutMixins.column}
gap: 0.5ch;
svg {
height: 0.75em;
width: 0.75em;
}
`;
Styled.ArrowIcon = styled(Icon)<{ direction: 'up' | 'down'; color: 'green' | 'red' }>`
${({ direction }) =>
({
['up']: css`
transform: rotate(-90deg);
`,
['down']: css`
transform: rotate(90deg);
`,
}[direction])}
${({ color }) =>
({
['green']: css`
color: var(--color-success);
`,
['red']: css`
color: var(--color-error);
`,
}[color])}
`;
const indicatorStyle = css`
--indicator-size: 1.25rem;
--icon-size: 0.5rem;
@ -182,9 +282,33 @@ const indicatorStyle = css`
justify-content: center;
`;
Styled.DotIndicator = styled.div<{ $selected: boolean }>`
${indicatorStyle}
--background-color: var(--color-layer-2);
--border-color: var(--color-border);
${({ $selected }) =>
$selected &&
css`
--background-color: var(--color-accent);
--border-color: var(--color-accent);
&::after {
content: '';
display: block;
width: var(--icon-size);
height: var(--icon-size);
background-color: var(--color-layer-2);
border-radius: 50%;
}
`}
background-color: var(--background-color);
border: solid var(--border-width) var(--border-color);
`;
Styled.CheckIndicator = styled(Indicator)`
${indicatorStyle}
position: absolute;
bottom: var(--item-padding);
right: var(--item-padding);

View File

@ -323,7 +323,7 @@ Styled.ReceiptArea = styled.div`
`;
Styled.Green = styled.span`
color: var(--color-positive);
color: var(--color-success);
`;
Styled.GreenCheckCircle = styled(GreenCheckCircle)`

View File

@ -453,6 +453,6 @@ Styled.FormInputButton = styled(Button)`
Styled.CheckIcon = styled(Icon)`
margin: 0 1ch;
color: var(--color-positive);
color: var(--color-success);
font-size: 0.625rem;
`;

View File

@ -441,7 +441,7 @@ Styled.DestinationInputLabel = styled.span`
${layoutMixins.inlineRow}
svg {
color: var(--color-positive);
color: var(--color-success);
}
`;

View File

@ -203,7 +203,7 @@ export const AccountMenu = () => {
value: 'MnemonicExport',
icon: <Icon iconName={IconName.ExportKeys} />,
label: <span>{stringGetter({ key: STRING_KEYS.EXPORT_SECRET_PHRASE })}</span>,
highlightColor: 'negative',
highlightColor: 'destroy',
onSelect: () => dispatch(openDialog({ type: DialogTypes.MnemonicExport })),
},
].filter(isTruthy)
@ -212,7 +212,7 @@ export const AccountMenu = () => {
value: 'Disconnect',
icon: <Icon iconName={IconName.BoxClose} />,
label: stringGetter({ key: STRING_KEYS.DISCONNECT }),
highlightColor: 'negative',
highlightColor: 'destroy',
onSelect: () => dispatch(openDialog({ type: DialogTypes.DisconnectWallet })),
},
].filter(isTruthy)}

View File

@ -72,7 +72,7 @@ Styled.Notification = styled(Notification)`
Styled.Output = styled(Output)`
&:before {
content: '+';
color: var(--color-positive);
color: var(--color-success);
margin-right: 0.5ch;
}
`;

View File

@ -179,7 +179,7 @@ Styled.Icon = styled.div<{ state: 'complete' | 'default' }>`
${({ state }) =>
({
['complete']: css`
color: var(--color-positive);
color: var(--color-success);
`,
['default']: css`
color: var(--color-text-0);

View File

@ -82,6 +82,6 @@ Styled.ActionButton = styled(IconButton)`
Styled.CancelButton = styled(Styled.ActionButton)`
&:not(:disabled) {
--button-textColor: var(--color-negative);
--button-textColor: var(--color-error);
}
`;