aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--webui/src/components/BugTitleForm/BugTitleForm.tsx38
-rw-r--r--webui/src/components/BugTitleForm/BugTitleInput.tsx40
-rw-r--r--webui/src/components/CloseBugButton/CloseBugButton.tsx11
-rw-r--r--webui/src/components/Header/Header.tsx7
-rw-r--r--webui/src/components/Themer.tsx65
-rw-r--r--webui/src/index.tsx9
-rw-r--r--webui/src/pages/bug/Message.tsx10
-rw-r--r--webui/src/pages/list/Filter.tsx4
-rw-r--r--webui/src/pages/list/FilterToolbar.tsx4
-rw-r--r--webui/src/pages/list/ListQuery.tsx30
-rw-r--r--webui/src/pages/new/NewBugPage.tsx27
-rw-r--r--webui/src/theme.ts11
-rw-r--r--webui/src/themes/DefaultDark.ts25
-rw-r--r--webui/src/themes/DefaultLight.ts24
-rw-r--r--webui/src/themes/index.ts4
15 files changed, 220 insertions, 89 deletions
diff --git a/webui/src/components/BugTitleForm/BugTitleForm.tsx b/webui/src/components/BugTitleForm/BugTitleForm.tsx
index c47eab31..c31f8ef7 100644
--- a/webui/src/components/BugTitleForm/BugTitleForm.tsx
+++ b/webui/src/components/BugTitleForm/BugTitleForm.tsx
@@ -1,12 +1,6 @@
import React, { useState } from 'react';
-import {
- Button,
- fade,
- makeStyles,
- TextField,
- Typography,
-} from '@material-ui/core';
+import { Button, makeStyles, Typography } from '@material-ui/core';
import { TimelineDocument } from '../../pages/bug/TimelineQuery.generated';
import IfLoggedIn from '../IfLoggedIn/IfLoggedIn';
@@ -14,6 +8,7 @@ import Author from 'src/components/Author';
import Date from 'src/components/Date';
import { BugFragment } from 'src/pages/bug/Bug.generated';
+import BugTitleInput from './BugTitleInput';
import { useSetTitleMutation } from './SetTitle.generated';
/**
@@ -45,26 +40,12 @@ const useStyles = makeStyles((theme) => ({
marginLeft: theme.spacing(2),
},
greenButton: {
- marginLeft: '8px',
- backgroundColor: '#2ea44fd9',
- color: '#fff',
- '&:hover': {
- backgroundColor: '#2ea44f',
- },
+ marginLeft: theme.spacing(1),
+ backgroundColor: theme.palette.success.main,
+ color: theme.palette.success.contrastText,
},
- titleInput: {
- borderRadius: theme.shape.borderRadius,
- borderColor: fade(theme.palette.primary.main, 0.2),
- borderStyle: 'solid',
- borderWidth: '1px',
- backgroundColor: fade(theme.palette.primary.main, 0.05),
- padding: theme.spacing(0, 0),
- minWidth: 336,
- transition: theme.transitions.create([
- 'width',
- 'borderColor',
- 'backgroundColor',
- ]),
+ saveButton: {
+ marginRight: theme.spacing(1),
},
}));
@@ -122,11 +103,11 @@ function BugTitleForm({ bug }: Props) {
function editableBugTitle() {
return (
<form className={classes.headerTitle} onSubmit={submitNewTitle}>
- <TextField
+ <BugTitleInput
inputRef={(node) => {
issueTitleInput = node;
}}
- className={classes.titleInput}
+ label="Title"
variant="outlined"
fullWidth
margin="dense"
@@ -135,6 +116,7 @@ function BugTitleForm({ bug }: Props) {
/>
<div className={classes.editButtonContainer}>
<Button
+ className={classes.saveButton}
size="small"
variant="contained"
type="submit"
diff --git a/webui/src/components/BugTitleForm/BugTitleInput.tsx b/webui/src/components/BugTitleForm/BugTitleInput.tsx
new file mode 100644
index 00000000..d2b060a2
--- /dev/null
+++ b/webui/src/components/BugTitleForm/BugTitleInput.tsx
@@ -0,0 +1,40 @@
+import { createStyles, fade, withStyles, TextField } from '@material-ui/core';
+import { Theme } from '@material-ui/core/styles';
+
+const BugTitleInput = withStyles((theme: Theme) =>
+ createStyles({
+ root: {
+ '& .MuiInputLabel-outlined': {
+ color: theme.palette.text.primary,
+ },
+ '& input:valid + fieldset': {
+ color: theme.palette.text.primary,
+ borderColor: theme.palette.divider,
+ borderWidth: 2,
+ },
+ '& input:valid:hover + fieldset': {
+ color: theme.palette.text.primary,
+ borderColor: fade(theme.palette.divider, 0.3),
+ borderWidth: 2,
+ },
+ '& input:valid:focus + fieldset': {
+ color: theme.palette.text.primary,
+ borderColor: theme.palette.divider,
+ },
+ '& input:invalid + fieldset': {
+ borderColor: theme.palette.error.main,
+ borderWidth: 2,
+ },
+ '& input:invalid:hover + fieldset': {
+ borderColor: theme.palette.error.main,
+ borderWidth: 2,
+ },
+ '& input:invalid:focus + fieldset': {
+ borderColor: theme.palette.error.main,
+ borderWidth: 2,
+ },
+ },
+ })
+)(TextField);
+
+export default BugTitleInput;
diff --git a/webui/src/components/CloseBugButton/CloseBugButton.tsx b/webui/src/components/CloseBugButton/CloseBugButton.tsx
index 19f56cab..8d397c23 100644
--- a/webui/src/components/CloseBugButton/CloseBugButton.tsx
+++ b/webui/src/components/CloseBugButton/CloseBugButton.tsx
@@ -1,12 +1,21 @@
import React from 'react';
import Button from '@material-ui/core/Button';
+import { makeStyles, Theme } from '@material-ui/core/styles';
+import ErrorOutlineIcon from '@material-ui/icons/ErrorOutline';
import { BugFragment } from 'src/pages/bug/Bug.generated';
import { TimelineDocument } from 'src/pages/bug/TimelineQuery.generated';
import { useCloseBugMutation } from './CloseBug.generated';
+const useStyles = makeStyles((theme: Theme) => ({
+ closeIssueIcon: {
+ color: theme.palette.secondary.dark,
+ paddingTop: '0.1rem',
+ },
+}));
+
interface Props {
bug: BugFragment;
disabled: boolean;
@@ -14,6 +23,7 @@ interface Props {
function CloseBugButton({ bug, disabled }: Props) {
const [closeBug, { loading, error }] = useCloseBugMutation();
+ const classes = useStyles();
function closeBugAction() {
closeBug({
@@ -45,6 +55,7 @@ function CloseBugButton({ bug, disabled }: Props) {
variant="contained"
onClick={() => closeBugAction()}
disabled={bug.status === 'CLOSED' || disabled}
+ startIcon={<ErrorOutlineIcon className={classes.closeIssueIcon} />}
>
Close issue
</Button>
diff --git a/webui/src/components/Header/Header.tsx b/webui/src/components/Header/Header.tsx
index 3e39b5f3..3bdb252f 100644
--- a/webui/src/components/Header/Header.tsx
+++ b/webui/src/components/Header/Header.tsx
@@ -5,6 +5,7 @@ import AppBar from '@material-ui/core/AppBar';
import Toolbar from '@material-ui/core/Toolbar';
import { makeStyles } from '@material-ui/core/styles';
+import { LightSwitch } from '../../components/Themer';
import CurrentIdentity from '../CurrentIdentity/CurrentIdentity';
const useStyles = makeStyles((theme) => ({
@@ -21,6 +22,9 @@ const useStyles = makeStyles((theme) => ({
display: 'flex',
alignItems: 'center',
},
+ lightSwitch: {
+ padding: '0 20px',
+ },
logo: {
height: '42px',
marginRight: theme.spacing(2),
@@ -39,6 +43,9 @@ function Header() {
git-bug
</Link>
<div className={classes.filler}></div>
+ <div className={classes.lightSwitch}>
+ <LightSwitch />
+ </div>
<CurrentIdentity />
</Toolbar>
</AppBar>
diff --git a/webui/src/components/Themer.tsx b/webui/src/components/Themer.tsx
new file mode 100644
index 00000000..b4877974
--- /dev/null
+++ b/webui/src/components/Themer.tsx
@@ -0,0 +1,65 @@
+import React, { createContext, useContext, useState } from 'react';
+
+import { fade, ThemeProvider } from '@material-ui/core';
+import IconButton from '@material-ui/core/IconButton/IconButton';
+import Tooltip from '@material-ui/core/Tooltip/Tooltip';
+import { Theme } from '@material-ui/core/styles';
+import { NightsStayRounded, WbSunnyRounded } from '@material-ui/icons';
+import { makeStyles } from '@material-ui/styles';
+
+const ThemeContext = createContext({
+ toggleMode: () => {},
+ mode: '',
+});
+
+const useStyles = makeStyles((theme: Theme) => ({
+ iconButton: {
+ color: fade(theme.palette.primary.contrastText, 0.5),
+ },
+}));
+
+const LightSwitch = () => {
+ const { mode, toggleMode } = useContext(ThemeContext);
+ const nextMode = mode === 'light' ? 'dark' : 'light';
+ const description = `Switch to ${nextMode} theme`;
+ const classes = useStyles();
+
+ return (
+ <Tooltip title={description}>
+ <IconButton
+ onClick={toggleMode}
+ aria-label={description}
+ className={classes.iconButton}
+ >
+ {mode === 'light' ? <WbSunnyRounded /> : <NightsStayRounded />}
+ </IconButton>
+ </Tooltip>
+ );
+};
+
+type Props = {
+ children: React.ReactNode;
+ lightTheme: Theme;
+ darkTheme: Theme;
+};
+const Themer = ({ children, lightTheme, darkTheme }: Props) => {
+ const savedMode = localStorage.getItem('themeMode');
+ const preferedMode = savedMode != null ? savedMode : 'light';
+ const [mode, setMode] = useState(preferedMode);
+
+ const toggleMode = () => {
+ const preferedMode = mode === 'light' ? 'dark' : 'light';
+ localStorage.setItem('themeMode', preferedMode);
+ setMode(preferedMode);
+ };
+
+ const preferedTheme = mode === 'dark' ? darkTheme : lightTheme;
+
+ return (
+ <ThemeContext.Provider value={{ toggleMode: toggleMode, mode: mode }}>
+ <ThemeProvider theme={preferedTheme}>{children}</ThemeProvider>
+ </ThemeContext.Provider>
+ );
+};
+
+export { Themer as default, LightSwitch };
diff --git a/webui/src/index.tsx b/webui/src/index.tsx
index f07b869d..d3591e1a 100644
--- a/webui/src/index.tsx
+++ b/webui/src/index.tsx
@@ -3,18 +3,17 @@ import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
-import ThemeProvider from '@material-ui/styles/ThemeProvider';
-
import App from './App';
import apolloClient from './apollo';
-import theme from './theme';
+import Themer from './components/Themer';
+import { defaultLightTheme, defaultDarkTheme } from './themes/index';
ReactDOM.render(
<ApolloProvider client={apolloClient}>
<BrowserRouter>
- <ThemeProvider theme={theme}>
+ <Themer lightTheme={defaultLightTheme} darkTheme={defaultDarkTheme}>
<App />
- </ThemeProvider>
+ </Themer>
</BrowserRouter>
</ApolloProvider>,
document.getElementById('root')
diff --git a/webui/src/pages/bug/Message.tsx b/webui/src/pages/bug/Message.tsx
index 91549483..faff5356 100644
--- a/webui/src/pages/bug/Message.tsx
+++ b/webui/src/pages/bug/Message.tsx
@@ -27,11 +27,13 @@ const useStyles = makeStyles((theme) => ({
},
header: {
...theme.typography.body1,
- color: '#444',
padding: '0.5rem 1rem',
- borderBottom: '1px solid #ddd',
+ borderBottom: `1px solid ${theme.palette.divider}`,
display: 'flex',
- backgroundColor: '#e2f1ff',
+ borderTopRightRadius: theme.shape.borderRadius,
+ borderTopLeftRadius: theme.shape.borderRadius,
+ backgroundColor: theme.palette.info.main,
+ color: theme.palette.info.contrastText,
},
title: {
flex: 1,
@@ -47,7 +49,7 @@ const useStyles = makeStyles((theme) => ({
},
body: {
...theme.typography.body2,
- padding: '0 1rem',
+ padding: '0.5rem',
},
}));
diff --git a/webui/src/pages/list/Filter.tsx b/webui/src/pages/list/Filter.tsx
index 5c4a3d17..66702078 100644
--- a/webui/src/pages/list/Filter.tsx
+++ b/webui/src/pages/list/Filter.tsx
@@ -65,7 +65,7 @@ function stringify(params: Query): string {
const useStyles = makeStyles((theme) => ({
element: {
...theme.typography.body2,
- color: '#444',
+ color: theme.palette.text.secondary,
padding: theme.spacing(0, 1),
fontWeight: 400,
textDecoration: 'none',
@@ -75,7 +75,7 @@ const useStyles = makeStyles((theme) => ({
},
itemActive: {
fontWeight: 600,
- color: '#333',
+ color: theme.palette.text.primary,
},
icon: {
paddingRight: theme.spacing(0.5),
diff --git a/webui/src/pages/list/FilterToolbar.tsx b/webui/src/pages/list/FilterToolbar.tsx
index 21626416..e4cd8e6a 100644
--- a/webui/src/pages/list/FilterToolbar.tsx
+++ b/webui/src/pages/list/FilterToolbar.tsx
@@ -19,8 +19,8 @@ import { useBugCountQuery } from './FilterToolbar.generated';
const useStyles = makeStyles((theme) => ({
toolbar: {
- backgroundColor: theme.palette.grey['100'],
- borderColor: theme.palette.grey['300'],
+ backgroundColor: theme.palette.primary.light,
+ borderColor: theme.palette.divider,
borderWidth: '1px 0',
borderStyle: 'solid',
margin: theme.spacing(0, -1),
diff --git a/webui/src/pages/list/ListQuery.tsx b/webui/src/pages/list/ListQuery.tsx
index 87c21e3c..fec7c33b 100644
--- a/webui/src/pages/list/ListQuery.tsx
+++ b/webui/src/pages/list/ListQuery.tsx
@@ -6,7 +6,7 @@ import { Button } from '@material-ui/core';
import IconButton from '@material-ui/core/IconButton';
import InputBase from '@material-ui/core/InputBase';
import Paper from '@material-ui/core/Paper';
-import { fade, makeStyles, Theme } from '@material-ui/core/styles';
+import { makeStyles, Theme } from '@material-ui/core/styles';
import ErrorOutline from '@material-ui/icons/ErrorOutline';
import KeyboardArrowLeft from '@material-ui/icons/KeyboardArrowLeft';
import KeyboardArrowRight from '@material-ui/icons/KeyboardArrowRight';
@@ -56,10 +56,11 @@ const useStyles = makeStyles<Theme, StylesProps>((theme) => ({
},
search: {
borderRadius: theme.shape.borderRadius,
- borderColor: fade(theme.palette.primary.main, 0.2),
+ color: theme.palette.text.secondary,
+ borderColor: theme.palette.divider,
borderStyle: 'solid',
borderWidth: '1px',
- backgroundColor: fade(theme.palette.primary.main, 0.05),
+ backgroundColor: theme.palette.primary.light,
padding: theme.spacing(0, 1),
width: ({ searching }) => (searching ? '20rem' : '15rem'),
transition: theme.transitions.create([
@@ -69,13 +70,11 @@ const useStyles = makeStyles<Theme, StylesProps>((theme) => ({
]),
},
searchFocused: {
- borderColor: fade(theme.palette.primary.main, 0.4),
backgroundColor: theme.palette.background.paper,
- width: '20rem!important',
},
placeholderRow: {
padding: theme.spacing(1),
- borderBottomColor: theme.palette.grey['300'],
+ borderBottomColor: theme.palette.divider,
borderBottomWidth: '1px',
borderBottomStyle: 'solid',
display: 'flex',
@@ -91,7 +90,8 @@ const useStyles = makeStyles<Theme, StylesProps>((theme) => ({
...theme.typography.h5,
padding: theme.spacing(8),
textAlign: 'center',
- borderBottomColor: theme.palette.grey['300'],
+ color: theme.palette.text.hint,
+ borderBottomColor: theme.palette.divider,
borderBottomWidth: '1px',
borderBottomStyle: 'solid',
'& > p': {
@@ -99,22 +99,22 @@ const useStyles = makeStyles<Theme, StylesProps>((theme) => ({
},
},
errorBox: {
- color: theme.palette.error.main,
+ color: theme.palette.error.dark,
'& > pre': {
fontSize: '1rem',
textAlign: 'left',
- backgroundColor: theme.palette.grey['900'],
- color: theme.palette.common.white,
+ borderColor: theme.palette.divider,
+ borderWidth: '1px',
+ borderRadius: theme.shape.borderRadius,
+ borderStyle: 'solid',
+ color: theme.palette.text.primary,
marginTop: theme.spacing(4),
padding: theme.spacing(2, 3),
},
},
greenButton: {
- backgroundColor: '#2ea44fd9',
- color: '#fff',
- '&:hover': {
- backgroundColor: '#2ea44f',
- },
+ backgroundColor: theme.palette.success.main,
+ color: theme.palette.success.contrastText,
},
}));
diff --git a/webui/src/pages/new/NewBugPage.tsx b/webui/src/pages/new/NewBugPage.tsx
index c9e268b6..a46226ad 100644
--- a/webui/src/pages/new/NewBugPage.tsx
+++ b/webui/src/pages/new/NewBugPage.tsx
@@ -2,9 +2,9 @@ import React, { FormEvent, useState } from 'react';
import { Button } from '@material-ui/core';
import Paper from '@material-ui/core/Paper';
-import TextField from '@material-ui/core/TextField/TextField';
-import { fade, makeStyles, Theme } from '@material-ui/core/styles';
+import { makeStyles, Theme } from '@material-ui/core/styles';
+import BugTitleInput from '../../components/BugTitleForm/BugTitleInput';
import CommentInput from '../../components/CommentInput/CommentInput';
import { useNewBugMutation } from './NewBug.generated';
@@ -21,19 +21,6 @@ const useStyles = makeStyles((theme: Theme) => ({
padding: theme.spacing(2),
overflow: 'hidden',
},
- titleInput: {
- borderRadius: theme.shape.borderRadius,
- borderColor: fade(theme.palette.primary.main, 0.2),
- borderStyle: 'solid',
- borderWidth: '1px',
- backgroundColor: fade(theme.palette.primary.main, 0.05),
- padding: theme.spacing(0, 0),
- transition: theme.transitions.create([
- 'width',
- 'borderColor',
- 'backgroundColor',
- ]),
- },
form: {
display: 'flex',
flexDirection: 'column',
@@ -43,11 +30,8 @@ const useStyles = makeStyles((theme: Theme) => ({
justifyContent: 'flex-end',
},
greenButton: {
- backgroundColor: '#2ea44fd9',
- color: '#fff',
- '&:hover': {
- backgroundColor: '#2ea44f',
- },
+ backgroundColor: theme.palette.success.main,
+ color: theme.palette.success.contrastText,
},
}));
@@ -85,12 +69,11 @@ function NewBugPage() {
return (
<Paper className={classes.main}>
<form className={classes.form} onSubmit={submitNewIssue}>
- <TextField
+ <BugTitleInput
inputRef={(node) => {
issueTitleInput = node;
}}
label="Title"
- className={classes.titleInput}
variant="outlined"
fullWidth
margin="dense"
diff --git a/webui/src/theme.ts b/webui/src/theme.ts
deleted file mode 100644
index d41cd731..00000000
--- a/webui/src/theme.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { createMuiTheme } from '@material-ui/core/styles';
-
-const theme = createMuiTheme({
- palette: {
- primary: {
- main: '#263238',
- },
- },
-});
-
-export default theme;
diff --git a/webui/src/themes/DefaultDark.ts b/webui/src/themes/DefaultDark.ts
new file mode 100644
index 00000000..6a92ec49
--- /dev/null
+++ b/webui/src/themes/DefaultDark.ts
@@ -0,0 +1,25 @@
+import { createMuiTheme } from '@material-ui/core/styles';
+
+const defaultDarkTheme = createMuiTheme({
+ palette: {
+ type: 'dark',
+ primary: {
+ main: '#263238',
+ light: '#525252',
+ },
+ error: {
+ main: '#f44336',
+ dark: '#ff4949',
+ },
+ info: {
+ main: '#2a393e',
+ contrastText: '#ffffffb3',
+ },
+ success: {
+ main: '#2ea44fd9',
+ contrastText: '#fff',
+ },
+ },
+});
+
+export default defaultDarkTheme;
diff --git a/webui/src/themes/DefaultLight.ts b/webui/src/themes/DefaultLight.ts
new file mode 100644
index 00000000..bc788a98
--- /dev/null
+++ b/webui/src/themes/DefaultLight.ts
@@ -0,0 +1,24 @@
+import { createMuiTheme } from '@material-ui/core/styles';
+
+const defaultLightTheme = createMuiTheme({
+ palette: {
+ type: 'light',
+ primary: {
+ main: '#263238',
+ light: '#f5f5f5',
+ },
+ info: {
+ main: '#e2f1ff',
+ contrastText: '#555',
+ },
+ success: {
+ main: '#2ea44fd9',
+ contrastText: '#fff',
+ },
+ text: {
+ secondary: '#555',
+ },
+ },
+});
+
+export default defaultLightTheme;
diff --git a/webui/src/themes/index.ts b/webui/src/themes/index.ts
new file mode 100644
index 00000000..6c41c546
--- /dev/null
+++ b/webui/src/themes/index.ts
@@ -0,0 +1,4 @@
+import defaultDarkTheme from './DefaultDark';
+import defaultLightTheme from './DefaultLight';
+
+export { defaultLightTheme, defaultDarkTheme };