mirror of
https://github.com/Unleash/unleash.git
synced 2025-04-24 01:18:01 +02:00
Feat/dark mode exp (#1137)
* feat: add dark mode theme * fix: feature metrics * fix: add color * styling * fix: add switch * fix: form sidebar * fix: remove console log * fix: add properties * fix: strategy container * feat: feature flag * fix: tests * fix: build * fix: logo * fix: icon * fix: update snapshots * fix: CES operator * fix: typography * fix: input styling * fix: remove initial load * fix: change flag name * fix: refactor to custom hook * fix: remove unused import * fix: dialog headers * fix: use uiConfig flags instead of flags
This commit is contained in:
parent
e6b72ff4a0
commit
6818a82cd1
@ -39,6 +39,7 @@
|
||||
"isready": "yarn lint && yarn fmt && yarn prepare"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@codemirror/lang-json": "6.0.0",
|
||||
"@emotion/react": "11.9.3",
|
||||
"@emotion/styled": "11.9.3",
|
||||
"@mui/icons-material": "5.8.4",
|
||||
@ -62,6 +63,7 @@
|
||||
"@types/react-test-renderer": "17.0.2",
|
||||
"@types/react-timeago": "4.1.3",
|
||||
"@types/semver": "7.3.12",
|
||||
"@uiw/react-codemirror": "4.11.4",
|
||||
"@vitejs/plugin-react": "1.3.2",
|
||||
"chart.js": "3.9.1",
|
||||
"chartjs-adapter-date-fns": "2.0.0",
|
||||
@ -102,8 +104,7 @@
|
||||
"vite-tsconfig-paths": "3.5.0",
|
||||
"vitest": "0.22.1",
|
||||
"whatwg-fetch": "3.6.2",
|
||||
"@codemirror/lang-json": "6.0.0",
|
||||
"@uiw/react-codemirror": "4.11.4"
|
||||
"@uiw/codemirror-theme-duotone": "^4.11.5"
|
||||
},
|
||||
"jest": {
|
||||
"moduleNameMapper": {
|
||||
|
1
frontend/src/assets/img/logoWithWhiteText.svg
Normal file
1
frontend/src/assets/img/logoWithWhiteText.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg id="bg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 653.4 251.24"><defs><style>.cls-1{fill:#fff;}.cls-2{fill:#1a4049;}.cls-3{fill:#817afe;}</style></defs><circle class="cls-1" cx="125.62" cy="125.62" r="80"/><polygon class="cls-1" points="137.05 91.33 137.05 114.19 137.05 137.05 159.9 137.05 159.9 114.19 159.9 91.33 137.05 91.33"/><polygon class="cls-1" points="114.19 114.19 114.19 91.33 91.33 91.33 91.33 114.19 91.33 137.05 91.33 159.9 114.19 159.9 137.05 159.9 137.05 137.05 114.19 137.05 114.19 114.19"/><polygon class="cls-2" points="137.05 91.33 137.05 114.19 137.05 137.05 159.9 137.05 159.9 114.19 159.9 91.33 137.05 91.33"/><polygon class="cls-2" points="114.19 114.19 114.19 91.33 91.33 91.33 91.33 114.19 91.33 137.05 91.33 159.9 114.19 159.9 137.05 159.9 137.05 137.05 114.19 137.05 114.19 114.19"/><rect class="cls-3" x="137.05" y="137.05" width="22.86" height="22.86"/><path class="cls-1" d="M251.58,139.13V112.77h11.93v25.06c0,7.36,3.91,12.2,11.27,12.2s11.27-4.84,11.27-12.2V112.77h12v26.36c0,12.67-8.48,21.8-23.1,21.8C260.06,160.93,251.58,151.8,251.58,139.13Z"/><path class="cls-1" d="M321.91,159.9H310.08V112.77h11.83v7.92a17.93,17.93,0,0,1,15.65-9c11.83,0,19.66,8.67,19.66,20.68V159.9h-12V134.75c0-7.45-4.38-12.39-11.46-12.39s-11.83,5-11.83,12.39Z"/><path class="cls-1" d="M369.42,91.34h11.92V159.9H369.42Z"/><path class="cls-1" d="M441.24,137v1.3H403.79c.47,7.36,5.87,13,13.69,13,7.55,0,10.62-3.82,11.46-5.12h11.74c-.75,4.84-7.17,15-23.2,15-15.27,0-25.61-10.61-25.61-24.77,0-14.63,10.24-24.78,24.68-24.78S441.24,121.62,441.24,137Zm-37.08-6.9h24.6c-1.77-6-6.15-9.31-12.21-9.31C410.12,120.78,405.65,124.23,404.16,130.09Z"/><path class="cls-1" d="M467.78,130.37h15.47c0-5.68-4.29-9.31-11.27-9.31-6.62,0-9,3.54-9.78,4.75h-12c1-4.94,6.89-14.25,21.8-14.25,14.62,0,22.73,7.64,22.73,18.81V146.3c0,2.89,1,4,3.72,4.29v9.59h-3.72c-6.06-.09-9.69-2.33-11-6.52-2.23,3.45-7.26,7.27-15.27,7.27-11.09,0-19.47-6.24-19.47-15.93S456.14,130.37,467.78,130.37Zm15.47,12.11v-4H469.74c-5.59,0-9,2-9,6.33,0,4.57,4.29,7.27,10.25,7.27C477.66,152.08,483.25,148.82,483.25,142.48Z"/><path class="cls-1" d="M523.12,140.62c-10.34-.74-17.33-5.59-17.33-14.72,0-8.85,8.1-14.44,20.77-14.44,17,0,21.8,8.76,23.1,13.32H537.09c-.84-1-3.54-4.28-10.62-4.28-5.68,0-8.66,1.86-8.66,4.75,0,2.61,2,4.29,6.7,4.94l8,.84c12.77,1.11,18,6,18,15.27,0,8.85-7.36,14.91-21.8,14.91-17.51,0-23.19-10.16-24.12-14.53h12.76c.47,1.11,3.35,5.31,11.36,5.31,6.62,0,9.69-2.24,9.69-5.22s-1.67-4.66-7-5.31C528.05,141.18,526.38,141,523.12,140.62Z"/><path class="cls-1" d="M571.55,159.9H559.72V91.34h11.83v29.35a17.93,17.93,0,0,1,15.65-9c11.83,0,19.66,8.67,19.66,20.68V159.9h-12V134.75c0-7.45-4.38-12.39-11.46-12.39s-11.83,5-11.83,12.39Z"/></svg>
|
After Width: | Height: | Size: 2.6 KiB |
@ -17,7 +17,7 @@ export const useStyles = makeStyles()(theme => ({
|
||||
margin: '0 auto',
|
||||
flex: 1,
|
||||
width: '100%',
|
||||
backgroundColor: theme.palette.grey[300],
|
||||
backgroundColor: theme.palette.contentWrapper,
|
||||
},
|
||||
content: {
|
||||
width: '1250px',
|
||||
|
@ -10,7 +10,7 @@ export const useStyles = makeStyles()(theme => ({
|
||||
},
|
||||
},
|
||||
icon: {
|
||||
background: theme.palette.primary.main,
|
||||
background: theme.palette.featureSegmentSearchBackground,
|
||||
height: 48,
|
||||
width: 48,
|
||||
display: 'flex',
|
||||
|
@ -2,7 +2,7 @@ import { makeStyles } from 'tss-react/mui';
|
||||
|
||||
export const useStyles = makeStyles()(theme => ({
|
||||
badge: {
|
||||
backgroundColor: theme.palette.primary.main,
|
||||
backgroundColor: theme.palette.checkmarkBadge,
|
||||
width: '75px',
|
||||
height: '75px',
|
||||
borderRadius: '50px',
|
||||
|
@ -2,7 +2,7 @@ import { makeStyles } from 'tss-react/mui';
|
||||
|
||||
export const useStyles = makeStyles()(theme => ({
|
||||
container: {
|
||||
backgroundColor: theme.palette.sidebarContainer,
|
||||
backgroundColor: theme.palette.codebox,
|
||||
padding: '1rem',
|
||||
borderRadius: theme.shape.borderRadiusMedium,
|
||||
position: 'relative',
|
||||
@ -13,7 +13,7 @@ export const useStyles = makeStyles()(theme => ({
|
||||
margin: 0,
|
||||
wordBreak: 'break-all',
|
||||
whiteSpace: 'pre-wrap',
|
||||
color: '#fff',
|
||||
color: theme.palette.formSidebarTextColor,
|
||||
fontSize: 14,
|
||||
},
|
||||
icon: {
|
||||
|
@ -1,10 +1,25 @@
|
||||
import { makeStyles } from 'tss-react/mui';
|
||||
|
||||
export const useStyles = makeStyles()(theme => ({
|
||||
constraintIconContainer: {
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
borderRadius: '50%',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
marginRight: theme.spacing(1),
|
||||
[theme.breakpoints.down(650)]: {
|
||||
marginBottom: '1rem',
|
||||
marginRight: 0,
|
||||
},
|
||||
},
|
||||
constraintIcon: {
|
||||
fill: '#fff',
|
||||
},
|
||||
accordion: {
|
||||
border: `1px solid ${theme.palette.dividerAlternative}`,
|
||||
borderRadius: theme.shape.borderRadiusMedium,
|
||||
backgroundColor: '#fff',
|
||||
backgroundColor: theme.palette.constraintAccordion.background,
|
||||
boxShadow: 'none',
|
||||
margin: 0,
|
||||
},
|
||||
@ -14,7 +29,7 @@ export const useStyles = makeStyles()(theme => ({
|
||||
},
|
||||
},
|
||||
accordionEdit: {
|
||||
backgroundColor: '#F6F6FA',
|
||||
backgroundColor: theme.palette.constraintAccordion.editBackground,
|
||||
},
|
||||
headerMetaInfo: {
|
||||
display: 'flex',
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Box, Tooltip } from '@mui/material';
|
||||
import { Box, Tooltip, useTheme } from '@mui/material';
|
||||
import { ReactComponent as NegatedIcon } from 'assets/icons/24_Negator.svg';
|
||||
import { ReactComponent as NegatedIconOff } from 'assets/icons/24_Negator off.svg';
|
||||
import { IConstraint } from 'interfaces/strategy';
|
||||
@ -7,6 +7,7 @@ import {
|
||||
StyledToggleButtonOn,
|
||||
} from '../StyledToggleButton';
|
||||
import { ConditionallyRender } from '../../../../ConditionallyRender/ConditionallyRender';
|
||||
import { ThemeMode } from 'component/common/ThemeMode/ThemeMode';
|
||||
|
||||
interface InvertedOperatorButtonProps {
|
||||
localConstraint: IConstraint;
|
||||
@ -16,7 +17,10 @@ interface InvertedOperatorButtonProps {
|
||||
export const InvertedOperatorButton = ({
|
||||
localConstraint,
|
||||
setInvertedOperator,
|
||||
}: InvertedOperatorButtonProps) => (
|
||||
}: InvertedOperatorButtonProps) => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
title={
|
||||
Boolean(localConstraint.inverted)
|
||||
@ -33,7 +37,17 @@ export const InvertedOperatorButton = ({
|
||||
onClick={setInvertedOperator}
|
||||
disableRipple
|
||||
>
|
||||
<NegatedIcon />
|
||||
<ThemeMode
|
||||
darkmode={
|
||||
<NegatedIcon
|
||||
style={{
|
||||
fill: theme.palette.background
|
||||
.paper,
|
||||
}}
|
||||
/>
|
||||
}
|
||||
lightmode={<NegatedIcon />}
|
||||
/>
|
||||
</StyledToggleButtonOn>
|
||||
}
|
||||
elseShow={
|
||||
@ -41,10 +55,21 @@ export const InvertedOperatorButton = ({
|
||||
onClick={setInvertedOperator}
|
||||
disableRipple
|
||||
>
|
||||
<NegatedIconOff />
|
||||
<ThemeMode
|
||||
darkmode={
|
||||
<NegatedIconOff
|
||||
style={{
|
||||
fill: theme.palette.background
|
||||
.paper,
|
||||
}}
|
||||
/>
|
||||
}
|
||||
lightmode={<NegatedIconOff />}
|
||||
/>
|
||||
</StyledToggleButtonOff>
|
||||
}
|
||||
/>
|
||||
</Box>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
@ -14,6 +14,7 @@ export const ConstraintIcon: VFC<IConstraintIconProps> = ({ compact }) => (
|
||||
borderRadius: '50%',
|
||||
width: compact ? '18px' : '24px',
|
||||
height: compact ? '18px' : '24px',
|
||||
marginRight: '13px',
|
||||
}}
|
||||
>
|
||||
<TrackChanges
|
||||
|
@ -4,7 +4,7 @@ export const useStyles = makeStyles()(theme => ({
|
||||
container: {
|
||||
padding: theme.spacing(0.5, 1.5),
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
backgroundColor: theme.palette.grey[200],
|
||||
backgroundColor: theme.palette.constraintAccordion.operatorBackground,
|
||||
lineHeight: 1.25,
|
||||
},
|
||||
name: {
|
||||
|
@ -2,8 +2,8 @@ import { makeStyles } from 'tss-react/mui';
|
||||
|
||||
export const useStyles = makeStyles()(theme => ({
|
||||
dialogTitle: {
|
||||
backgroundColor: theme.palette.primary.main,
|
||||
color: '#fff',
|
||||
backgroundColor: theme.palette.dialogHeaderBackground,
|
||||
color: theme.palette.dialogHeaderText,
|
||||
height: '150px',
|
||||
padding: '2rem 3rem',
|
||||
clipPath: ' ellipse(130% 115px at 120% 20%)',
|
||||
|
@ -20,7 +20,7 @@ export const useStyles = makeStyles()(theme => ({
|
||||
borderRadius: 0,
|
||||
},
|
||||
sidebar: {
|
||||
backgroundColor: theme.palette.primary.main,
|
||||
backgroundColor: theme.palette.formSidebar,
|
||||
padding: '2rem',
|
||||
flexGrow: 0,
|
||||
flexShrink: 0,
|
||||
@ -42,7 +42,7 @@ export const useStyles = makeStyles()(theme => ({
|
||||
fontWeight: 'normal',
|
||||
},
|
||||
subtitle: {
|
||||
color: '#fff',
|
||||
color: theme.palette.formSidebarTextColor,
|
||||
marginBottom: '1rem',
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
@ -51,7 +51,7 @@ export const useStyles = makeStyles()(theme => ({
|
||||
fontSize: theme.fontSizes.bodySize,
|
||||
},
|
||||
description: {
|
||||
color: '#fff',
|
||||
color: theme.palette.formSidebarTextColor,
|
||||
zIndex: 1,
|
||||
position: 'relative',
|
||||
},
|
||||
@ -72,7 +72,7 @@ export const useStyles = makeStyles()(theme => ({
|
||||
},
|
||||
},
|
||||
formContent: {
|
||||
backgroundColor: '#fff',
|
||||
backgroundColor: theme.palette.formBackground,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
padding: '3rem',
|
||||
|
@ -3,7 +3,7 @@ import React, { FC, VFC, useEffect, useState, useContext } from 'react';
|
||||
import { InstanceStatusBar } from 'component/common/InstanceStatus/InstanceStatusBar';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { Dialogue } from 'component/common/Dialogue/Dialogue';
|
||||
import { Typography } from '@mui/material';
|
||||
import { Typography, useTheme } from '@mui/material';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { IInstanceStatus } from 'interfaces/instance';
|
||||
import { ADMIN } from 'component/providers/AccessProvider/permissions';
|
||||
@ -91,6 +91,7 @@ export const InstanceStatus: FC = ({ children }) => {
|
||||
useInstanceStatus();
|
||||
const { extendTrial } = useInstanceStatusApi();
|
||||
const { setToastApiError } = useToast();
|
||||
const theme = useTheme();
|
||||
|
||||
const onExtendTrial = async () => {
|
||||
try {
|
||||
@ -102,7 +103,12 @@ export const InstanceStatus: FC = ({ children }) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ height: '100%' }}>
|
||||
<div
|
||||
style={{
|
||||
height: '100%',
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
}}
|
||||
>
|
||||
<ConditionallyRender
|
||||
condition={isBilling && Boolean(instanceStatus)}
|
||||
show={() => (
|
||||
|
@ -6,6 +6,7 @@ export const useStyles = makeStyles()(theme => ({
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
height: '100%',
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
},
|
||||
img: {
|
||||
width: '100px',
|
||||
|
@ -6,6 +6,7 @@ export const useStyles = makeStyles()(theme => ({
|
||||
flexGrow: 1,
|
||||
alignItems: 'center',
|
||||
position: 'relative',
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
maxWidth: '400px',
|
||||
[theme.breakpoints.down('md')]: {
|
||||
marginTop: theme.spacing(1),
|
||||
|
@ -7,7 +7,7 @@ export const useStyles = makeStyles()(theme => ({
|
||||
'& + &': {
|
||||
marginTop: theme.spacing(2),
|
||||
},
|
||||
background: theme.palette.background.default,
|
||||
background: theme.palette.background.paper,
|
||||
},
|
||||
header: {
|
||||
padding: theme.spacing(0.5, 2),
|
||||
|
@ -5,7 +5,8 @@ export const useStyles = makeStyles()(theme => ({
|
||||
'& > th': {
|
||||
height: theme.shape.tableRowHeightCompact,
|
||||
border: 0,
|
||||
|
||||
backgroundColor: theme.palette.tableHeaderBackground,
|
||||
color: theme.palette.tableHeaderColor,
|
||||
'&:first-of-type': {
|
||||
borderTopLeftRadius: theme.shape.borderRadiusMedium,
|
||||
borderBottomLeftRadius: theme.shape.borderRadiusMedium,
|
||||
|
20
frontend/src/component/common/ThemeMode/ThemeMode.tsx
Normal file
20
frontend/src/component/common/ThemeMode/ThemeMode.tsx
Normal file
@ -0,0 +1,20 @@
|
||||
import UIContext from 'contexts/UIContext';
|
||||
import { useContext } from 'react';
|
||||
import { ConditionallyRender } from '../ConditionallyRender/ConditionallyRender';
|
||||
|
||||
interface IThemeModeProps {
|
||||
darkmode: JSX.Element;
|
||||
lightmode: JSX.Element;
|
||||
}
|
||||
|
||||
export const ThemeMode = ({ darkmode, lightmode }: IThemeModeProps) => {
|
||||
const { themeMode } = useContext(UIContext);
|
||||
|
||||
return (
|
||||
<ConditionallyRender
|
||||
condition={themeMode === 'dark'}
|
||||
show={darkmode}
|
||||
elseShow={lightmode}
|
||||
/>
|
||||
);
|
||||
};
|
@ -3,7 +3,7 @@ import { makeStyles } from 'tss-react/mui';
|
||||
export const useStyles = makeStyles()(theme => ({
|
||||
container: {
|
||||
maxWidth: '450px',
|
||||
background: '#fff',
|
||||
background: theme.palette.background.paper,
|
||||
boxShadow: '2px 2px 4px rgba(0,0,0,0.4)',
|
||||
zIndex: 500,
|
||||
margin: '0 0.8rem',
|
||||
|
@ -10,7 +10,7 @@ export const useStyles = makeStyles()(theme => ({
|
||||
paddingBlockStart: 4,
|
||||
paddingBlockEnd: 4,
|
||||
borderRadius: '100rem',
|
||||
background: theme.palette.primary.main,
|
||||
background: theme.palette.featureStrategySegmentChipBackground,
|
||||
color: 'white',
|
||||
},
|
||||
link: {
|
||||
|
@ -3,7 +3,7 @@ import { makeStyles } from 'tss-react/mui';
|
||||
export const useStyles = makeStyles()(theme => ({
|
||||
container: {
|
||||
borderRadius: '12.5px',
|
||||
backgroundColor: '#fff',
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
padding: '2rem',
|
||||
},
|
||||
}));
|
||||
|
@ -3,7 +3,7 @@ import { makeStyles } from 'tss-react/mui';
|
||||
export const useStyles = makeStyles()(theme => ({
|
||||
item: {
|
||||
padding: theme.spacing(2),
|
||||
background: theme.palette.secondaryContainer,
|
||||
background: theme.palette.featureMetricsBackground,
|
||||
borderRadius: theme.spacing(2),
|
||||
textAlign: 'center',
|
||||
[theme.breakpoints.up('md')]: {
|
||||
|
@ -9,7 +9,7 @@ import { styled } from '@mui/material';
|
||||
|
||||
const StyledContainer = styled('div')(({ theme }) => ({
|
||||
borderRadius: theme.shape.borderRadiusLarge,
|
||||
backgroundColor: '#fff',
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
padding: '1.5rem',
|
||||
|
@ -4,7 +4,8 @@ export const useStyles = makeStyles()(theme => ({
|
||||
featureOverviewEnvironment: {
|
||||
borderRadius: theme.shape.borderRadiusLarge,
|
||||
marginBottom: theme.spacing(2),
|
||||
background: theme.palette.background.default,
|
||||
padding: '0.2rem',
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
},
|
||||
accordion: {
|
||||
boxShadow: 'none',
|
||||
|
@ -53,7 +53,7 @@ const FeatureOverviewEnvironment = ({
|
||||
style={{
|
||||
background: !env.enabled
|
||||
? theme.palette.neutral.light
|
||||
: theme.palette.background.default,
|
||||
: theme.palette.background.paper,
|
||||
}}
|
||||
>
|
||||
<Accordion
|
||||
|
@ -4,7 +4,7 @@ export const useStyles = makeStyles()(theme => ({
|
||||
container: {
|
||||
borderRadius: theme.shape.borderRadiusLarge,
|
||||
color: '#fff',
|
||||
backgroundColor: theme.palette.primary.main,
|
||||
backgroundColor: theme.palette.featureMetaData,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
maxWidth: '350px',
|
||||
|
@ -7,7 +7,7 @@ export const useStyles = makeStyles()(theme => ({
|
||||
display: 'flex',
|
||||
},
|
||||
header: {
|
||||
backgroundColor: '#fff',
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
borderRadius: theme.shape.borderRadiusLarge,
|
||||
marginBottom: '1rem',
|
||||
},
|
||||
|
@ -15,7 +15,7 @@ export const useStyles = makeStyles()(theme => ({
|
||||
pointerEvents: 'auto',
|
||||
position: 'relative',
|
||||
padding: '4rem',
|
||||
background: 'white',
|
||||
background: theme.palette.background.paper,
|
||||
boxShadow: '0 0 1rem rgba(0, 0, 0, 0.25)',
|
||||
borderRadius: '1rem',
|
||||
[theme.breakpoints.down('md')]: {
|
||||
|
@ -131,18 +131,18 @@ exports[`FeedbackCESForm 1`] = `
|
||||
class="MuiFormControl-root MuiFormControl-fullWidth MuiTextField-root mui-wb57ya-MuiFormControl-root-MuiTextField-root"
|
||||
>
|
||||
<div
|
||||
class="MuiInputBase-root MuiOutlinedInput-root MuiInputBase-colorPrimary MuiInputBase-fullWidth MuiInputBase-formControl MuiInputBase-multiline mui-181wpo2-MuiInputBase-root-MuiOutlinedInput-root"
|
||||
class="MuiInputBase-root MuiOutlinedInput-root MuiInputBase-colorPrimary MuiInputBase-fullWidth MuiInputBase-formControl MuiInputBase-multiline mui-1qvtpcv-MuiInputBase-root-MuiOutlinedInput-root"
|
||||
>
|
||||
<textarea
|
||||
aria-invalid="false"
|
||||
class="MuiInputBase-input MuiOutlinedInput-input MuiInputBase-inputMultiline mui-1632cvv-MuiInputBase-input-MuiOutlinedInput-input"
|
||||
class="MuiInputBase-input MuiOutlinedInput-input MuiInputBase-inputMultiline mui-1wvz5kg-MuiInputBase-input-MuiOutlinedInput-input"
|
||||
id="mui-1"
|
||||
rows="3"
|
||||
style="height: 0px; overflow: hidden;"
|
||||
/>
|
||||
<textarea
|
||||
aria-hidden="true"
|
||||
class="MuiInputBase-input MuiOutlinedInput-input MuiInputBase-inputMultiline mui-1632cvv-MuiInputBase-input-MuiOutlinedInput-input"
|
||||
class="MuiInputBase-input MuiOutlinedInput-input MuiInputBase-inputMultiline mui-1wvz5kg-MuiInputBase-input-MuiOutlinedInput-input"
|
||||
readonly=""
|
||||
style="visibility: hidden; position: absolute; overflow: hidden; height: 0px; top: 0px; left: 0px; transform: translateZ(0); padding: 0px; width: 100%;"
|
||||
tabindex="-1"
|
||||
|
@ -3,7 +3,7 @@ import { makeStyles } from 'tss-react/mui';
|
||||
export const useStyles = makeStyles()(theme => ({
|
||||
feedback: {
|
||||
borderRadius: '12.5px',
|
||||
backgroundColor: '#fff',
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
zIndex: 9999,
|
||||
boxShadow: '2px 2px 4px 4px rgba(143,143,143, 0.25)',
|
||||
padding: '1.5rem',
|
||||
@ -21,7 +21,7 @@ export const useStyles = makeStyles()(theme => ({
|
||||
position: 'absolute',
|
||||
right: '-38px',
|
||||
top: '-47px',
|
||||
backgroundColor: '#fff',
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
boxShadow: '2px 2px 4px 4px rgba(143,143,143, 0.25)',
|
||||
['&:hover']: {
|
||||
backgroundColor: '#fff',
|
||||
|
@ -6,6 +6,7 @@ export const useStyles = makeStyles()(theme => ({
|
||||
width: '100%',
|
||||
flexGrow: 1,
|
||||
zIndex: 100,
|
||||
backgroundColor: theme.palette.footerBackground,
|
||||
},
|
||||
list: {
|
||||
padding: 0,
|
||||
|
@ -3,7 +3,7 @@
|
||||
exports[`should render DrawerMenu 1`] = `
|
||||
[
|
||||
<footer
|
||||
className="tss-wd65c0-footer"
|
||||
className="tss-16d7ppb-footer"
|
||||
>
|
||||
<div
|
||||
className="MuiGrid-root MuiGrid-container MuiGrid-spacing-xs-10 mui-16chest-MuiGrid-root"
|
||||
@ -448,7 +448,7 @@ exports[`should render DrawerMenu 1`] = `
|
||||
exports[`should render DrawerMenu with "features" selected 1`] = `
|
||||
[
|
||||
<footer
|
||||
className="tss-wd65c0-footer"
|
||||
className="tss-16d7ppb-footer"
|
||||
>
|
||||
<div
|
||||
className="MuiGrid-root MuiGrid-container MuiGrid-spacing-xs-10 mui-16chest-MuiGrid-root"
|
||||
|
@ -2,8 +2,7 @@ import { makeStyles } from 'tss-react/mui';
|
||||
|
||||
export const useStyles = makeStyles()(theme => ({
|
||||
header: {
|
||||
backgroundColor: '#fff',
|
||||
color: '#000',
|
||||
backgroundColor: theme.palette.headerBackground,
|
||||
padding: '0.5rem',
|
||||
boxShadow: 'none',
|
||||
position: 'relative',
|
||||
@ -80,7 +79,6 @@ export const useStyles = makeStyles()(theme => ({
|
||||
textDecoration: 'none',
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
color: '#000',
|
||||
},
|
||||
icon: {
|
||||
color: theme.palette.grey[700],
|
||||
|
@ -2,13 +2,21 @@ import { useEffect, useState, VFC } from 'react';
|
||||
import useMediaQuery from '@mui/material/useMediaQuery';
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { AppBar, Container, IconButton, Tooltip } from '@mui/material';
|
||||
import {
|
||||
AppBar,
|
||||
Container,
|
||||
FormControlLabel,
|
||||
IconButton,
|
||||
Tooltip,
|
||||
Switch,
|
||||
} from '@mui/material';
|
||||
import MenuIcon from '@mui/icons-material/Menu';
|
||||
import SettingsIcon from '@mui/icons-material/Settings';
|
||||
import UserProfile from 'component/user/UserProfile';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import MenuBookIcon from '@mui/icons-material/MenuBook';
|
||||
import { ReactComponent as UnleashLogo } from 'assets/img/logoDarkWithText.svg';
|
||||
import { ReactComponent as UnleashLogoWhite } from 'assets/img/logoWithWhiteText.svg';
|
||||
|
||||
import { DrawerMenu } from './DrawerMenu/DrawerMenu';
|
||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
@ -24,8 +32,11 @@ import { useStyles } from './Header.styles';
|
||||
import classNames from 'classnames';
|
||||
import { useId } from 'hooks/useId';
|
||||
import { IRoute } from 'interfaces/route';
|
||||
import { ThemeMode } from 'component/common/ThemeMode/ThemeMode';
|
||||
import { useThemeMode } from 'hooks/useThemeMode';
|
||||
|
||||
const Header: VFC = () => {
|
||||
const { onSetThemeMode, themeMode } = useThemeMode();
|
||||
const theme = useTheme();
|
||||
const adminId = useId();
|
||||
const configId = useId();
|
||||
@ -111,10 +122,20 @@ const Header: VFC = () => {
|
||||
)}
|
||||
aria-label="Home"
|
||||
>
|
||||
<ThemeMode
|
||||
darkmode={
|
||||
<UnleashLogoWhite
|
||||
className={styles.logo}
|
||||
aria-label="Unleash logo"
|
||||
/>
|
||||
}
|
||||
lightmode={
|
||||
<UnleashLogo
|
||||
className={styles.logo}
|
||||
aria-label="Unleash logo"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</Link>
|
||||
<nav className={styles.nav}>
|
||||
<div className={styles.links}>
|
||||
@ -148,6 +169,22 @@ const Header: VFC = () => {
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.userContainer}>
|
||||
<ConditionallyRender
|
||||
condition={Boolean(
|
||||
uiConfig.flags.ENABLE_DARK_MODE_SUPPORT
|
||||
)}
|
||||
show={
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
onChange={onSetThemeMode}
|
||||
checked={themeMode === 'dark'}
|
||||
/>
|
||||
}
|
||||
label="darkmode"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Tooltip title="Documentation" arrow>
|
||||
<IconButton
|
||||
href="https://docs.getunleash.io/"
|
||||
|
@ -20,7 +20,7 @@ export const useStyles = makeStyles()(theme => ({
|
||||
textDecoration: 'none',
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
color: '#000',
|
||||
color: 'inherit',
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
'&&': {
|
||||
|
@ -159,7 +159,7 @@ export const Playground: VFC<{}> = () => {
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
background: theme.palette.grey[200],
|
||||
background: theme.palette.playgroundBackground,
|
||||
borderBottomLeftRadius: theme.shape.borderRadiusMedium,
|
||||
}}
|
||||
>
|
||||
@ -170,7 +170,7 @@ export const Playground: VFC<{}> = () => {
|
||||
py: 3,
|
||||
mb: 4,
|
||||
mt: 2,
|
||||
background: theme.palette.grey[200],
|
||||
background: theme.palette.playgroundBackground,
|
||||
transition: 'width 0.4s ease',
|
||||
minWidth: matches ? 'auto' : '500px',
|
||||
width: formWidth,
|
||||
|
@ -1,10 +1,13 @@
|
||||
import CodeMirror from '@uiw/react-codemirror';
|
||||
import { useContext } from 'react';
|
||||
import { json } from '@codemirror/lang-json';
|
||||
import { Dispatch, SetStateAction, VFC, useCallback } from 'react';
|
||||
import { styled, useTheme, Box } from '@mui/material';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { duotoneDark, duotoneLight } from '@uiw/codemirror-theme-duotone';
|
||||
import Check from '@mui/icons-material/Check';
|
||||
import { Error } from '@mui/icons-material';
|
||||
import UIContext from 'contexts/UIContext';
|
||||
|
||||
interface IPlaygroundEditorProps {
|
||||
context: string | undefined;
|
||||
@ -78,6 +81,7 @@ export const PlaygroundEditor: VFC<IPlaygroundEditorProps> = ({
|
||||
setContext,
|
||||
error,
|
||||
}) => {
|
||||
const { themeMode } = useContext(UIContext);
|
||||
const theme = useTheme();
|
||||
const onCodeFieldChange = useCallback(
|
||||
context => {
|
||||
@ -109,6 +113,7 @@ export const PlaygroundEditor: VFC<IPlaygroundEditorProps> = ({
|
||||
<CodeMirror
|
||||
value={context}
|
||||
height="200px"
|
||||
theme={themeMode === 'dark' ? duotoneDark : duotoneLight}
|
||||
extensions={[json()]}
|
||||
onChange={onCodeFieldChange}
|
||||
style={{
|
||||
|
@ -13,7 +13,7 @@ export const useStyles = makeStyles()(theme => ({
|
||||
minWidth: 0,
|
||||
},
|
||||
header: {
|
||||
backgroundColor: '#fff',
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
borderRadius: theme.shape.borderRadiusLarge,
|
||||
marginBottom: '1rem',
|
||||
},
|
||||
|
@ -55,7 +55,7 @@ export const useStyles = makeStyles()(theme => ({
|
||||
margin: '0',
|
||||
textAlign: 'center',
|
||||
marginBottom: '1rem',
|
||||
backgroundColor: '#fff',
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
borderRadius: theme.shape.borderRadiusLarge,
|
||||
width: '100%',
|
||||
padding: '1.5rem 1rem 1.5rem 1rem',
|
||||
|
@ -16,7 +16,7 @@ export const useStyles = makeStyles()(theme => ({
|
||||
},
|
||||
'&:hover': {
|
||||
transition: 'background-color 0.2s ease-in-out',
|
||||
backgroundColor: theme.palette.neutral.light,
|
||||
backgroundColor: theme.palette.projectCard.hover,
|
||||
},
|
||||
},
|
||||
header: {
|
||||
@ -49,7 +49,7 @@ export const useStyles = makeStyles()(theme => ({
|
||||
textAlign: 'center',
|
||||
},
|
||||
infoStats: {
|
||||
color: '#4A4599',
|
||||
color: theme.palette.projectCard.textColor,
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
actionsBtn: {
|
||||
|
@ -1,10 +1,36 @@
|
||||
import React, { useState } from 'react';
|
||||
import UIContext, { createEmptyToast } from 'contexts/UIContext';
|
||||
import UIContext, { createEmptyToast, themeMode } from 'contexts/UIContext';
|
||||
import { IToast } from 'interfaces/toast';
|
||||
import { getLocalStorageItem } from 'utils/storage';
|
||||
|
||||
const UIProvider: React.FC = ({ children }) => {
|
||||
const resolveMode = (darkmode: boolean): themeMode => {
|
||||
const value = getLocalStorageItem('unleash-theme');
|
||||
if (value) {
|
||||
return value as themeMode;
|
||||
}
|
||||
|
||||
let osDark;
|
||||
if (darkmode) {
|
||||
osDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
}
|
||||
|
||||
if (osDark) {
|
||||
return 'dark';
|
||||
}
|
||||
return 'light';
|
||||
};
|
||||
|
||||
interface IUiProviderProps {
|
||||
darkmode: boolean;
|
||||
}
|
||||
|
||||
const UIProvider: React.FC<IUiProviderProps> = ({
|
||||
children,
|
||||
darkmode = false,
|
||||
}) => {
|
||||
const [toastData, setToast] = useState<IToast>(createEmptyToast());
|
||||
const [showFeedback, setShowFeedback] = useState(false);
|
||||
const [themeMode, setThemeMode] = useState(resolveMode(darkmode));
|
||||
|
||||
const context = React.useMemo(
|
||||
() => ({
|
||||
@ -12,8 +38,10 @@ const UIProvider: React.FC = ({ children }) => {
|
||||
toastData,
|
||||
showFeedback,
|
||||
setShowFeedback,
|
||||
themeMode,
|
||||
setThemeMode,
|
||||
}),
|
||||
[toastData, showFeedback]
|
||||
[toastData, showFeedback, themeMode]
|
||||
);
|
||||
|
||||
return <UIContext.Provider value={context}>{children}</UIContext.Provider>;
|
||||
|
@ -0,0 +1,16 @@
|
||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
import UIProvider from './UIProvider';
|
||||
|
||||
export const UIProviderContainer: React.FC = ({ children }) => {
|
||||
const { uiConfig } = useUiConfig();
|
||||
|
||||
if (!uiConfig.flags) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<UIProvider darkmode={uiConfig.flags.ENABLE_DARK_MODE_SUPPORT || false}>
|
||||
{children}
|
||||
</UIProvider>
|
||||
);
|
||||
};
|
@ -17,7 +17,7 @@ export const useStyles = makeStyles()(theme => ({
|
||||
steps: {
|
||||
position: 'relative',
|
||||
borderRadius: 10,
|
||||
background: '#fff',
|
||||
background: theme.palette.background.paper,
|
||||
padding: '0.6rem 1.5rem',
|
||||
margin: 'auto',
|
||||
display: 'flex',
|
||||
|
@ -13,7 +13,7 @@ test('renders an empty list correctly', () => {
|
||||
<MemoryRouter>
|
||||
<ThemeProvider>
|
||||
<AnnouncerProvider>
|
||||
<UIProvider>
|
||||
<UIProvider darkmode={false}>
|
||||
<AccessProviderMock
|
||||
permissions={[{ permission: ADMIN }]}
|
||||
>
|
||||
|
@ -7,7 +7,7 @@ exports[`renders an empty list correctly 1`] = `
|
||||
aria-live="polite"
|
||||
>
|
||||
<div
|
||||
className="MuiPaper-root MuiPaper-elevation MuiPaper-rounded MuiPaper-elevation1 tss-15wj2kz-container mui-177gdp-MuiPaper-root"
|
||||
className="MuiPaper-root MuiPaper-elevation MuiPaper-rounded MuiPaper-elevation1 tss-15wj2kz-container mui-lorrhs-MuiPaper-root"
|
||||
>
|
||||
<div
|
||||
className="header tss-1ywhhai-headerContainer"
|
||||
@ -32,7 +32,7 @@ exports[`renders an empty list correctly 1`] = `
|
||||
className="tss-u5t8ea-headerActions"
|
||||
>
|
||||
<div
|
||||
className="tss-119iiqp-container"
|
||||
className="tss-1oozr04-container"
|
||||
>
|
||||
<div
|
||||
className="tss-1xjrf9m-search search-container"
|
||||
@ -49,12 +49,12 @@ exports[`renders an empty list correctly 1`] = `
|
||||
/>
|
||||
</svg>
|
||||
<div
|
||||
className="MuiInputBase-root tss-11gf6cf-inputRoot input-container MuiInputBase-colorPrimary mui-1v3pvzz-MuiInputBase-root"
|
||||
className="MuiInputBase-root tss-11gf6cf-inputRoot input-container MuiInputBase-colorPrimary mui-qtv7d9-MuiInputBase-root"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<input
|
||||
aria-label="Search (Ctrl+K)"
|
||||
className="MuiInputBase-input mui-j79lc6-MuiInputBase-input"
|
||||
className="MuiInputBase-input mui-mfsqjb-MuiInputBase-input"
|
||||
onAnimationStart={[Function]}
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
@ -120,7 +120,7 @@ exports[`renders an empty list correctly 1`] = `
|
||||
role={null}
|
||||
>
|
||||
<tr
|
||||
className="MuiTableRow-root MuiTableRow-head tss-1sk7xq7-tableHeader mui-cn4v9y-MuiTableRow-root"
|
||||
className="MuiTableRow-root MuiTableRow-head tss-1u2w2tj-tableHeader mui-cn4v9y-MuiTableRow-root"
|
||||
role="row"
|
||||
>
|
||||
<th
|
||||
|
@ -17,8 +17,8 @@ const StandaloneBanner: FC<IStandaloneBannerProps> = ({ title, children }) => {
|
||||
|
||||
return (
|
||||
<Gradient
|
||||
from={theme.palette.primary.main}
|
||||
to="#173341"
|
||||
from={theme.palette.standaloneBannerGradient.from}
|
||||
to={theme.palette.standaloneBannerGradient.to}
|
||||
className={styles.gradient}
|
||||
>
|
||||
<div className={styles.container}>
|
||||
|
@ -75,6 +75,7 @@ const UserProfile = ({
|
||||
disableRipple
|
||||
>
|
||||
<Avatar
|
||||
style={{ backgroundColor: '#fff', padding: '0.15rem' }}
|
||||
alt="Your Gravatar"
|
||||
src={imageUrl}
|
||||
data-testid={HEADER_USER_AVATAR}
|
||||
|
@ -20,6 +20,7 @@ import { basePath } from 'utils/formatPath';
|
||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
import { IUser } from 'interfaces/user';
|
||||
import { ILocationSettings } from 'hooks/useLocationSettings';
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
|
||||
interface IUserProfileContentProps {
|
||||
id: string;
|
||||
@ -45,6 +46,8 @@ const UserProfileContent = ({
|
||||
setLocationSettings,
|
||||
}: IUserProfileContentProps) => {
|
||||
const { classes: themeStyles } = useThemeStyles();
|
||||
const theme = useTheme();
|
||||
|
||||
const { uiConfig } = useUiConfig();
|
||||
const [updatedPassword, setUpdatedPassword] = useState(false);
|
||||
const [editingProfile, setEditingProfile] = useState(false);
|
||||
@ -121,7 +124,11 @@ const UserProfileContent = ({
|
||||
>
|
||||
<InputLabel
|
||||
htmlFor="locale-select"
|
||||
style={{ backgroundColor: '#fff' }}
|
||||
style={{
|
||||
backgroundColor:
|
||||
theme.palette
|
||||
.inputLabelBackground,
|
||||
}}
|
||||
>
|
||||
Date/Time formatting
|
||||
</InputLabel>
|
||||
|
@ -3,7 +3,7 @@ import { makeStyles } from 'tss-react/mui';
|
||||
export const useStyles = makeStyles()(theme => ({
|
||||
container: {
|
||||
padding: '5.5rem',
|
||||
background: '#EFF2F2',
|
||||
background: theme.palette.standaloneBackground,
|
||||
display: 'flex',
|
||||
[theme.breakpoints.down('md')]: {
|
||||
flexDirection: 'column',
|
||||
@ -27,7 +27,7 @@ export const useStyles = makeStyles()(theme => ({
|
||||
flex: '1',
|
||||
borderTopRightRadius: '3px',
|
||||
borderBottomRightRadius: '3px',
|
||||
backgroundColor: '#fff',
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
position: 'relative',
|
||||
[theme.breakpoints.down('md')]: {
|
||||
borderRadius: '0',
|
||||
|
@ -6,8 +6,12 @@ interface IUIContext {
|
||||
setToast: React.Dispatch<React.SetStateAction<IToast>>;
|
||||
showFeedback: boolean;
|
||||
setShowFeedback: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
setThemeMode: React.Dispatch<React.SetStateAction<themeMode>>;
|
||||
themeMode: themeMode;
|
||||
}
|
||||
|
||||
export type themeMode = 'light' | 'dark';
|
||||
|
||||
export const createEmptyToast = (): IToast => {
|
||||
return {
|
||||
type: 'success',
|
||||
@ -27,11 +31,17 @@ const setShowFeedbackPlaceholder = () => {
|
||||
throw new Error('setShowFeedback called outside UIContext');
|
||||
};
|
||||
|
||||
const setThemeModePlaceholder = () => {
|
||||
throw new Error('setMode called outside UIContext');
|
||||
};
|
||||
|
||||
const UIContext = React.createContext<IUIContext>({
|
||||
toastData: createEmptyToast(),
|
||||
setToast: setToastPlaceholder,
|
||||
showFeedback: false,
|
||||
setShowFeedback: setShowFeedbackPlaceholder,
|
||||
themeMode: 'light',
|
||||
setThemeMode: setThemeModePlaceholder,
|
||||
});
|
||||
|
||||
export default UIContext;
|
||||
|
37
frontend/src/hooks/useThemeMode.ts
Normal file
37
frontend/src/hooks/useThemeMode.ts
Normal file
@ -0,0 +1,37 @@
|
||||
import UIContext, { themeMode } from 'contexts/UIContext';
|
||||
import { useContext } from 'react';
|
||||
import { setLocalStorageItem } from 'utils/storage';
|
||||
import mainTheme from 'themes/theme';
|
||||
import darkTheme from 'themes/dark-theme';
|
||||
import { Theme } from '@emotion/react';
|
||||
|
||||
interface IUseThemeModeOutput {
|
||||
resolveTheme: () => Theme;
|
||||
onSetThemeMode: () => void;
|
||||
themeMode: themeMode;
|
||||
}
|
||||
|
||||
export const useThemeMode = (): IUseThemeModeOutput => {
|
||||
const { themeMode, setThemeMode } = useContext(UIContext);
|
||||
|
||||
const resolveTheme = () => {
|
||||
if (themeMode === 'light') {
|
||||
return mainTheme;
|
||||
}
|
||||
|
||||
return darkTheme;
|
||||
};
|
||||
|
||||
const onSetThemeMode = () => {
|
||||
setThemeMode((prev: themeMode) => {
|
||||
if (prev === 'light') {
|
||||
setLocalStorageItem('unleash-theme', 'dark');
|
||||
return 'dark';
|
||||
}
|
||||
setLocalStorageItem('unleash-theme', 'light');
|
||||
return 'light';
|
||||
});
|
||||
};
|
||||
|
||||
return { resolveTheme, onSetThemeMode, themeMode };
|
||||
};
|
@ -10,12 +10,12 @@ import { ScrollTop } from 'component/common/ScrollTop/ScrollTop';
|
||||
import { AccessProvider } from 'component/providers/AccessProvider/AccessProvider';
|
||||
import { basePath } from 'utils/formatPath';
|
||||
import { FeedbackCESProvider } from 'component/feedback/FeedbackCESContext/FeedbackCESProvider';
|
||||
import UIProvider from 'component/providers/UIProvider/UIProvider';
|
||||
import { AnnouncerProvider } from 'component/common/Announcer/AnnouncerProvider/AnnouncerProvider';
|
||||
import { InstanceStatus } from 'component/common/InstanceStatus/InstanceStatus';
|
||||
import { UIProviderContainer } from 'component/providers/UIProvider/UIProviderContainer';
|
||||
|
||||
ReactDOM.render(
|
||||
<UIProvider>
|
||||
<UIProviderContainer>
|
||||
<AccessProvider>
|
||||
<BrowserRouter basename={basePath}>
|
||||
<ThemeProvider>
|
||||
@ -30,6 +30,6 @@ ReactDOM.render(
|
||||
</ThemeProvider>
|
||||
</BrowserRouter>
|
||||
</AccessProvider>
|
||||
</UIProvider>,
|
||||
</UIProviderContainer>,
|
||||
document.getElementById('app')
|
||||
);
|
||||
|
@ -39,6 +39,7 @@ export interface IFlags {
|
||||
T?: boolean;
|
||||
UNLEASH_CLOUD?: boolean;
|
||||
UG?: boolean;
|
||||
ENABLE_DARK_MODE_SUPPORT?: boolean;
|
||||
}
|
||||
|
||||
export interface IVersionInfo {
|
||||
|
@ -1,19 +1,24 @@
|
||||
import React, { FC } from 'react';
|
||||
import { CssBaseline, ThemeProvider as MuiThemeProvider } from '@mui/material';
|
||||
import mainTheme from 'themes/theme';
|
||||
|
||||
import createCache from '@emotion/cache';
|
||||
import { CacheProvider } from '@emotion/react';
|
||||
import { useThemeMode } from 'hooks/useThemeMode';
|
||||
|
||||
export const muiCache = createCache({
|
||||
key: 'mui',
|
||||
prepend: true,
|
||||
});
|
||||
|
||||
export const ThemeProvider: FC = ({ children }) => (
|
||||
export const ThemeProvider: FC = ({ children }) => {
|
||||
const { resolveTheme } = useThemeMode();
|
||||
|
||||
return (
|
||||
<CacheProvider value={muiCache}>
|
||||
<MuiThemeProvider theme={mainTheme}>
|
||||
<MuiThemeProvider theme={resolveTheme()}>
|
||||
<CssBaseline />
|
||||
{children}
|
||||
</MuiThemeProvider>
|
||||
</CacheProvider>
|
||||
);
|
||||
};
|
||||
|
@ -17,10 +17,6 @@ button {
|
||||
font-family: 'Sen', sans-serif;
|
||||
}
|
||||
|
||||
.MuiInputBase-root {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.MuiAccordion-root.Mui-expanded {
|
||||
margin: 0;
|
||||
}
|
||||
|
@ -78,4 +78,12 @@ export const colors = {
|
||||
100: '#DCEEFA',
|
||||
50: '#EBF7FF',
|
||||
},
|
||||
darkblue: {
|
||||
1000: '#091826',
|
||||
900: '#041c32',
|
||||
800: '#1d3247',
|
||||
700: '#28415c',
|
||||
600: '#1f3751',
|
||||
500: '#0e2840',
|
||||
},
|
||||
} as const;
|
||||
|
418
frontend/src/themes/dark-theme.ts
Normal file
418
frontend/src/themes/dark-theme.ts
Normal file
@ -0,0 +1,418 @@
|
||||
import { createTheme } from '@mui/material/styles';
|
||||
import { colors } from './colors';
|
||||
|
||||
const themeColors = {
|
||||
main: colors.darkblue[900],
|
||||
secondary: colors.darkblue[800],
|
||||
textColor: '#ffffffe6',
|
||||
hover: 'rgb(255, 255, 255, 0.7)',
|
||||
};
|
||||
|
||||
export default createTheme({
|
||||
breakpoints: {
|
||||
values: {
|
||||
xs: 0,
|
||||
sm: 600,
|
||||
md: 960,
|
||||
lg: 1260,
|
||||
xl: 1536,
|
||||
},
|
||||
},
|
||||
boxShadows: {
|
||||
main: '0px 2px 4px rgba(129, 122, 254, 0.2)',
|
||||
card: '0px 2px 10px rgba(28, 25, 78, 0.12)',
|
||||
elevated: '0px 1px 20px rgba(45, 42, 89, 0.1)',
|
||||
},
|
||||
typography: {
|
||||
fontFamily: 'Sen, Roboto, sans-serif',
|
||||
fontWeightBold: '700',
|
||||
fontWeightMedium: '700',
|
||||
allVariants: { lineHeight: 1.4 },
|
||||
button: { lineHeight: 1.75 },
|
||||
h1: {
|
||||
fontSize: '1.5rem',
|
||||
lineHeight: 1.875,
|
||||
},
|
||||
h3: {
|
||||
fontSize: '1rem',
|
||||
fontWeight: '700',
|
||||
},
|
||||
caption: {
|
||||
fontSize: `${12 / 16}rem`,
|
||||
},
|
||||
},
|
||||
fontSizes: {
|
||||
mainHeader: '1.25rem',
|
||||
bodySize: '1rem',
|
||||
smallBody: `${14 / 16}rem`,
|
||||
smallerBody: `${12 / 16}rem`,
|
||||
},
|
||||
fontWeight: {
|
||||
thin: 300,
|
||||
medium: 400,
|
||||
semi: 700,
|
||||
bold: 700,
|
||||
},
|
||||
shape: {
|
||||
borderRadius: 4,
|
||||
borderRadiusMedium: 8,
|
||||
borderRadiusLarge: 12,
|
||||
borderRadiusExtraLarge: 20,
|
||||
tableRowHeight: 64,
|
||||
tableRowHeightCompact: 56,
|
||||
tableRowHeightDense: 48,
|
||||
},
|
||||
palette: {
|
||||
primary: {
|
||||
main: themeColors.textColor,
|
||||
light: colors.purple[700],
|
||||
dark: colors.purple[900],
|
||||
},
|
||||
secondary: {
|
||||
main: colors.purple[800],
|
||||
light: colors.purple[700],
|
||||
dark: colors.purple[900],
|
||||
},
|
||||
info: {
|
||||
light: colors.blue[50],
|
||||
main: colors.blue[500],
|
||||
dark: colors.blue[700],
|
||||
border: colors.blue[200],
|
||||
},
|
||||
success: {
|
||||
light: colors.green[50],
|
||||
main: colors.green[600],
|
||||
dark: themeColors.main,
|
||||
border: colors.green[300],
|
||||
},
|
||||
warning: {
|
||||
light: colors.orange[100],
|
||||
main: colors.orange[800],
|
||||
dark: colors.orange[900],
|
||||
border: colors.orange[500],
|
||||
},
|
||||
error: {
|
||||
light: colors.red[50],
|
||||
main: colors.red[700],
|
||||
dark: colors.red[800],
|
||||
border: colors.red[300],
|
||||
},
|
||||
background: {
|
||||
paper: themeColors.main,
|
||||
},
|
||||
divider: themeColors.secondary,
|
||||
dividerAlternative: colors.grey[400],
|
||||
tableHeaderHover: colors.darkblue[700],
|
||||
tableHeaderBackground: themeColors.secondary,
|
||||
tableHeaderColor: themeColors.textColor,
|
||||
highlight: '#FFEACC',
|
||||
secondaryContainer: themeColors.secondary,
|
||||
contentWrapper: colors.darkblue[800],
|
||||
formBackground: themeColors.main,
|
||||
formSidebar: colors.darkblue[1000],
|
||||
headerBackground: themeColors.main,
|
||||
footerBackground: themeColors.main,
|
||||
sidebarContainer: 'rgba(32,32,33, 0.2)',
|
||||
codebox: colors.darkblue[600],
|
||||
featureMetaData: colors.darkblue[1000],
|
||||
playgroundBackground: colors.darkblue[600],
|
||||
playgroundFormBackground: themeColors.secondary,
|
||||
standaloneBackground: colors.black,
|
||||
featureStrategySegmentChipBackground: themeColors.secondary,
|
||||
featureSegmentSearchBackground: themeColors.secondary,
|
||||
dialogHeaderBackground: themeColors.secondary,
|
||||
dialogHeaderText: themeColors.textColor,
|
||||
lightBorder: colors.darkblue[500],
|
||||
constraintAccordion: {
|
||||
editBackground: colors.darkblue[600],
|
||||
background: themeColors.secondary,
|
||||
operatorBackground: themeColors.main,
|
||||
},
|
||||
standaloneBannerGradient: {
|
||||
from: colors.darkblue[1000],
|
||||
to: colors.black,
|
||||
},
|
||||
projectCard: {
|
||||
hover: themeColors.secondary,
|
||||
textColor: themeColors.textColor,
|
||||
},
|
||||
formSidebarTextColor: themeColors.textColor,
|
||||
checkmarkBadge: themeColors.secondary,
|
||||
inputLabelBackground: 'transparent',
|
||||
featureMetricsBackground: themeColors.secondary,
|
||||
grey: colors.grey,
|
||||
text: {
|
||||
primary: themeColors.textColor,
|
||||
secondary: colors.grey[800],
|
||||
disabled: colors.grey[600],
|
||||
},
|
||||
code: {
|
||||
main: '#0b8c8f',
|
||||
diffAdd: 'green',
|
||||
diffSub: 'red',
|
||||
diffNeutral: 'black',
|
||||
edited: 'blue',
|
||||
},
|
||||
neutral: {
|
||||
light: colors.darkblue[500],
|
||||
main: colors.grey[700],
|
||||
dark: colors.grey[800],
|
||||
border: colors.grey[500],
|
||||
},
|
||||
activityIndicators: {
|
||||
primary: themeColors.secondary,
|
||||
unknown: themeColors.secondary,
|
||||
recent: themeColors.secondary,
|
||||
inactive: themeColors.secondary,
|
||||
abandoned: themeColors.secondary,
|
||||
},
|
||||
tertiary: {
|
||||
light: themeColors.secondary,
|
||||
main: colors.grey[400],
|
||||
dark: colors.grey[600],
|
||||
background: 'white',
|
||||
contrast: colors.grey[300],
|
||||
},
|
||||
inactiveIcon: colors.grey[600],
|
||||
},
|
||||
components: {
|
||||
MuiAppBar: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
color: themeColors.textColor,
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiInputLabel: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
color: themeColors.textColor,
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiIconButton: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
color: colors.white,
|
||||
'&:hover': {
|
||||
backgroundColor: themeColors.hover,
|
||||
},
|
||||
'&.Mui-disabled': {
|
||||
'& .MuiSvgIcon-root': {
|
||||
color: colors.grey[700],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiLink: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
color: colors.white,
|
||||
'&:hover': {
|
||||
textDecoration: 'none',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiBreadcrumbs: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
color: themeColors.textColor,
|
||||
fontSize: '0.875rem',
|
||||
'& a': {
|
||||
color: themeColors.textColor,
|
||||
textDecoration: 'underline',
|
||||
'&:hover': {
|
||||
textDecoration: 'none',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiTableHead: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
background: 'transparent',
|
||||
'& th': {
|
||||
background: colors.grey[200],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiTableRow: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
'&.MuiTableRow-hover:hover': {
|
||||
background: themeColors.secondary,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiTableCell: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
borderBottomColor: themeColors.main,
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiAlert: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
borderRadius: '8px',
|
||||
a: {
|
||||
color: 'inherit',
|
||||
},
|
||||
'&.MuiAlert-standardInfo': {
|
||||
backgroundColor: colors.blue[50],
|
||||
color: colors.blue[700],
|
||||
border: `1px solid ${colors.blue[200]}`,
|
||||
'& .MuiAlert-icon .MuiSvgIcon-root': {
|
||||
color: colors.blue[500],
|
||||
},
|
||||
},
|
||||
'&.MuiAlert-standardSuccess': {
|
||||
backgroundColor: colors.green[50],
|
||||
color: colors.green[800],
|
||||
border: `1px solid ${colors.green[300]}`,
|
||||
'& .MuiAlert-icon .MuiSvgIcon-root': {
|
||||
color: colors.green[500],
|
||||
},
|
||||
},
|
||||
'&.MuiAlert-standardWarning': {
|
||||
backgroundColor: colors.orange[100],
|
||||
color: colors.orange[900],
|
||||
border: `1px solid ${colors.orange[500]}`,
|
||||
'& .MuiAlert-icon .MuiSvgIcon-root': {
|
||||
color: colors.orange[800],
|
||||
},
|
||||
},
|
||||
'&.MuiAlert-standardError': {
|
||||
backgroundColor: colors.red[50],
|
||||
color: colors.red[800],
|
||||
border: `1px solid ${colors.red[300]}`,
|
||||
'& .MuiAlert-icon .MuiSvgIcon-root': {
|
||||
color: colors.red[700],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiButton: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
'&.Mui-disabled': {
|
||||
backgroundColor: colors.grey[400],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiSvgIcon: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
color: themeColors.textColor,
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiTabs: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
backgroundColor: themeColors.main,
|
||||
color: themeColors.textColor,
|
||||
'& .MuiTabs-indicator': {
|
||||
height: '4px',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiTab: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
color: themeColors.textColor,
|
||||
fontSize: '1rem',
|
||||
textTransform: 'none',
|
||||
fontWeight: 400,
|
||||
minHeight: '62px',
|
||||
'&:hover': {
|
||||
backgroundColor: themeColors.secondary,
|
||||
},
|
||||
'&.Mui-selected': {
|
||||
color: themeColors.textColor,
|
||||
fontWeight: 700,
|
||||
},
|
||||
'& > span': {
|
||||
color: colors.purple[900],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiAccordionSummary: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
'& > .MuiAccordionSummary-content.Mui-expanded': {
|
||||
margin: '12px 0',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiSwitch: {
|
||||
styleOverrides: {
|
||||
switchBase: {
|
||||
zIndex: 1,
|
||||
'&:not(.Mui-checked) .MuiTouchRipple-child': {
|
||||
color: colors.grey['500'],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiIcon: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
color: colors.white,
|
||||
},
|
||||
colorDisabled: {
|
||||
color: colors.white[600],
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiMenu: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
'.MuiMenu-list': {
|
||||
backgroundColor: colors.darkblue[600],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiMenuItem: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
'&.Mui-disabled': {
|
||||
opacity: 0.66,
|
||||
},
|
||||
'&:hover': {
|
||||
backgroundColor: themeColors.secondary,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiInputBase: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
backgroundColor: colors.darkblue[1000],
|
||||
'.MuiSvgIcon-root': {
|
||||
color: '#fff',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiPaper: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
backgroundColor: themeColors.main,
|
||||
color: themeColors.textColor,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
@ -107,9 +107,42 @@ export default createTheme({
|
||||
divider: colors.grey[300],
|
||||
dividerAlternative: colors.grey[400],
|
||||
tableHeaderHover: colors.grey[400],
|
||||
tableHeaderBackground: colors.grey[200],
|
||||
tableHeaderColor: colors.grey[900],
|
||||
formSidebarTextColor: colors.white,
|
||||
highlight: '#FFEACC',
|
||||
secondaryContainer: colors.grey[200],
|
||||
contentWrapper: colors.grey[300],
|
||||
headerBackground: colors.white,
|
||||
footerBackground: colors.white,
|
||||
formBackground: colors.white,
|
||||
formSidebar: colors.purple[800],
|
||||
featureMetaData: colors.purple[800],
|
||||
codebox: 'rgba(32,32,33, 0.2)',
|
||||
sidebarContainer: 'rgba(32,32,33, 0.2)',
|
||||
playgroundBackground: colors.grey[200],
|
||||
playgroundFormBackground: colors.grey[200],
|
||||
standaloneBackground: colors.grey[300],
|
||||
constraintAccordion: {
|
||||
editBackground: '#F6F6FA',
|
||||
background: colors.white,
|
||||
operatorBackground: colors.grey[200],
|
||||
},
|
||||
projectCard: {
|
||||
hover: colors.grey[100],
|
||||
textColor: '#4A4599',
|
||||
},
|
||||
standaloneBannerGradient: {
|
||||
from: colors.purple[900],
|
||||
to: '#173341',
|
||||
},
|
||||
checkmarkBadge: colors.purple[800],
|
||||
inputLabelBackground: colors.white,
|
||||
featureMetricsBackground: colors.grey[100],
|
||||
featureStrategySegmentChipBackground: colors.purple[800],
|
||||
featureSegmentSearchBackground: colors.purple[800],
|
||||
dialogHeaderBackground: colors.purple[800],
|
||||
dialogHeaderText: '#ffffffe6',
|
||||
grey: colors.grey,
|
||||
lightBorder: colors.grey[400],
|
||||
text: {
|
||||
@ -333,5 +366,26 @@ export default createTheme({
|
||||
}),
|
||||
},
|
||||
},
|
||||
MuiInputBase: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
backgroundColor: '#fff',
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiPaper: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
backgroundColor: '#fff',
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiAppBar: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
color: colors.black,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -54,10 +54,44 @@ declare module '@mui/material/styles' {
|
||||
primary: string;
|
||||
};
|
||||
dividerAlternative: string;
|
||||
contentWrapper: string;
|
||||
headerBackground: string;
|
||||
footerBackground: string;
|
||||
codebox: string;
|
||||
featureMetaData: string;
|
||||
playgroundBackground: string;
|
||||
playgroundFormBackground: string;
|
||||
standaloneBackground: string;
|
||||
standaloneBannerGradient: {
|
||||
from: string;
|
||||
to: string;
|
||||
};
|
||||
featureMetricsBackground: string;
|
||||
constraintAccordion: {
|
||||
editBackground: string;
|
||||
background: string;
|
||||
operatorBackground: string;
|
||||
};
|
||||
projectCard: {
|
||||
hover: string;
|
||||
textColor: string;
|
||||
};
|
||||
checkmarkBadge: string;
|
||||
inputLabelBackground: string;
|
||||
featureStrategySegmentChipBackground: string;
|
||||
featureSegmentSearchBackground: string;
|
||||
dialogHeaderBackground: string;
|
||||
dialogHeaderText: string;
|
||||
formSidebarTextColor: string;
|
||||
/**
|
||||
* For table header hover effect.
|
||||
*/
|
||||
tableHeaderBackground: string;
|
||||
tableHeaderHover: string;
|
||||
tableHeaderColor: string;
|
||||
|
||||
formBackground: string;
|
||||
formSidebar: string;
|
||||
/**
|
||||
* Text highlight effect color. Used when filtering/searching over content.
|
||||
*/
|
||||
|
@ -2447,6 +2447,22 @@
|
||||
"@codemirror/state" "^6.0.0"
|
||||
"@codemirror/view" "^6.0.0"
|
||||
|
||||
"@uiw/codemirror-theme-duotone@^4.11.5":
|
||||
version "4.11.5"
|
||||
resolved "https://registry.yarnpkg.com/@uiw/codemirror-theme-duotone/-/codemirror-theme-duotone-4.11.5.tgz#12df3666337f757253f810fc4e61c8bc887e40c6"
|
||||
integrity sha512-5Ea9naIp/KM6jZjog7tChdOjEPQgeKwSDuCh+TY1lQyt5YJS5P3eZSZq68KNZlwuBHj2c53h069brMnhguTFag==
|
||||
dependencies:
|
||||
"@uiw/codemirror-themes" "4.11.5"
|
||||
|
||||
"@uiw/codemirror-themes@4.11.5":
|
||||
version "4.11.5"
|
||||
resolved "https://registry.yarnpkg.com/@uiw/codemirror-themes/-/codemirror-themes-4.11.5.tgz#15926e65129b41c9ec607065416fc829970e122a"
|
||||
integrity sha512-f2i3yGPzxmCsvx4t6HHykcEhAxbI4GDAMzdFtQfFYMuu1F5sQP43JQvBC8ESflYk05QmE9rCWWZZpvOathLzVw==
|
||||
dependencies:
|
||||
"@codemirror/language" "^6.0.0"
|
||||
"@codemirror/state" "^6.0.0"
|
||||
"@codemirror/view" "^6.0.0"
|
||||
|
||||
"@uiw/react-codemirror@4.11.4":
|
||||
version "4.11.4"
|
||||
resolved "https://registry.yarnpkg.com/@uiw/react-codemirror/-/react-codemirror-4.11.4.tgz#76adc757baa0b8b1a9bd30d7081f5622b896d607"
|
||||
|
Loading…
Reference in New Issue
Block a user