diff options
-rw-r--r-- | webui/src/components/BugTitleForm/BugTitleForm.tsx | 38 | ||||
-rw-r--r-- | webui/src/components/BugTitleForm/BugTitleInput.tsx | 40 | ||||
-rw-r--r-- | webui/src/components/CloseBugButton/CloseBugButton.tsx | 11 | ||||
-rw-r--r-- | webui/src/components/Header/Header.tsx | 7 | ||||
-rw-r--r-- | webui/src/components/Themer.tsx | 65 | ||||
-rw-r--r-- | webui/src/index.tsx | 9 | ||||
-rw-r--r-- | webui/src/pages/bug/Message.tsx | 10 | ||||
-rw-r--r-- | webui/src/pages/list/Filter.tsx | 4 | ||||
-rw-r--r-- | webui/src/pages/list/FilterToolbar.tsx | 4 | ||||
-rw-r--r-- | webui/src/pages/list/ListQuery.tsx | 30 | ||||
-rw-r--r-- | webui/src/pages/new/NewBugPage.tsx | 27 | ||||
-rw-r--r-- | webui/src/theme.ts | 11 | ||||
-rw-r--r-- | webui/src/themes/DefaultDark.ts | 25 | ||||
-rw-r--r-- | webui/src/themes/DefaultLight.ts | 24 | ||||
-rw-r--r-- | webui/src/themes/index.ts | 4 |
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 }; |