Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/theme switcher #525

Merged
merged 12 commits into from
Jan 13, 2020
35 changes: 35 additions & 0 deletions apps/sensenet/src/components/ant-switch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { createStyles, Theme, withStyles } from '@material-ui/core/styles'
import { Switch } from '@material-ui/core'

export const AntSwitch = withStyles((theme: Theme) =>
createStyles({
root: {
width: 28,
height: 16,
padding: 0,
display: 'flex',
},
switchBase: {
padding: 2,
color: theme.palette.common.white,
opacity: '1',
'&$checked': {
transform: 'translateX(12px)',
color: theme.palette.common.black,
'& + $track': {
opacity: 1,
backgroundColor: theme.palette.common.white,
},
},
},
thumb: {
width: 12,
height: 12,
boxShadow: 'none',
},
track: {
opacity: 1,
},
checked: {},
}),
)(Switch)
8 changes: 6 additions & 2 deletions apps/sensenet/src/components/appbar/DesktopAppBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { ResponsiveContext, ResponsivePersonalSetttings } from '../../context'
import { useCommandPalette, useTheme } from '../../hooks'
import { CommandPalette } from '../command-palette/CommandPalette'
import { RepositorySelector } from '../RepositorySelector'
import { DesktopNavMenu } from './DesktopNavMenu'

export const DesktopAppBar: React.FunctionComponent<{ openDrawer?: () => void }> = props => {
const device = useContext(ResponsiveContext)
Expand All @@ -16,8 +17,10 @@ export const DesktopAppBar: React.FunctionComponent<{ openDrawer?: () => void }>
const commandPalette = useCommandPalette()

return (
<AppBar position="sticky" style={{ backgroundColor: theme.palette.background.paper }}>
<Toolbar>
<AppBar
position="sticky"
style={{ backgroundColor: theme.palette.background.paper, position: 'relative', height: '64px' }}>
<Toolbar style={{ position: 'static' }}>
<div
style={{
display: 'flex',
Expand All @@ -39,6 +42,7 @@ export const DesktopAppBar: React.FunctionComponent<{ openDrawer?: () => void }>
</div>

{personalSettings.commandPalette.enabled ? <CommandPalette {...commandPalette} /> : <div style={{ flex: 1 }} />}
<DesktopNavMenu />
</Toolbar>
</AppBar>
)
Expand Down
157 changes: 157 additions & 0 deletions apps/sensenet/src/components/appbar/DesktopNavMenu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import React from 'react'
import ClickAwayListener from '@material-ui/core/ClickAwayListener'
import {
Grid,
Grow,
IconButton,
ListItemIcon,
ListItemText,
MenuItem,
MenuList,
Paper,
Popper,
Typography,
} from '@material-ui/core'
import { createStyles, makeStyles, Theme, useTheme } from '@material-ui/core/styles'
import KeyboardArrowDown from '@material-ui/icons/KeyboardArrowDown'
import { useInjector, useRepository, useSession } from '@sensenet/hooks-react'
import { NavLink } from 'react-router-dom'
import { UserAvatar } from '../UserAvatar'
import { useLocalization, usePersonalSettings } from '../../hooks'
import { useDialog } from '../dialogs'
import { PersonalSettings } from '../../services'
import { AntSwitch } from '../../components/ant-switch'

const useStyles = makeStyles((theme: Theme) =>
createStyles({
root: {
display: 'flex',
height: '100%',
padding: '8px 16px',
background: theme.palette.background.paper,
alignItems: 'center',
},
paper: {
marginRight: theme.spacing(2),
},
}),
)

export const DesktopNavMenu: React.FunctionComponent = () => {
const personalSettings = usePersonalSettings()
const injector = useInjector()
const classes = useStyles()
const theme = useTheme()
const service = injector.getInstance(PersonalSettings)
const { openDialog } = useDialog()
const [open, setOpen] = React.useState(false)
const anchorRef = React.useRef<HTMLButtonElement>(null)
const session = useSession()
const repo = useRepository()
const localization = useLocalization()

const handleToggle = () => {
setOpen(prevOpen => !prevOpen)
}

const handleClose = (event: React.MouseEvent<EventTarget>) => {
if (anchorRef.current && anchorRef.current.contains(event.target as HTMLElement)) {
return
}

setOpen(false)
}

function handleListKeyDown(event: React.KeyboardEvent) {
if (event.key === 'Tab') {
event.preventDefault()
setOpen(false)
}
}

const prevOpen = React.useRef(open)
React.useEffect(() => {
if (prevOpen.current === true && open === false) {
anchorRef.current!.focus()
}

prevOpen.current = open
}, [open])

const logout = (event: React.MouseEvent<EventTarget>) => {
openDialog({ name: 'logout', props: { userToLogout: session.currentUser } })
handleClose(event)
}

const switchTheme = () => (event: React.ChangeEvent<HTMLInputElement>) => {
const settings = service.userValue.getValue()
service.setPersonalSettingsValue({ ...settings, theme: event.target.checked ? 'dark' : 'light' })
}

return (
<div className={classes.root}>
<UserAvatar user={session.currentUser} repositoryUrl={repo.configuration.repositoryUrl} />
<IconButton
ref={anchorRef}
aria-controls={open ? 'menu-list-grow' : undefined}
aria-haspopup="true"
onClick={handleToggle}
style={{ padding: '0' }}>
<KeyboardArrowDown />
</IconButton>
<Popper
style={{ top: '65px', left: 'unset', right: '1px', width: 'fit-content' }}
open={open}
role={undefined}
transition
disablePortal>
{({ TransitionProps, placement }) => (
<Grow
{...TransitionProps}
style={{
transformOrigin: placement === 'bottom' ? 'right top' : 'right bottom',
}}>
<Paper>
<ClickAwayListener onClickAway={handleClose}>
<MenuList autoFocusItem={open} id="menu-list-grow" onKeyDown={handleListKeyDown}>
<MenuItem onClick={handleClose}>
<ListItemIcon>
<UserAvatar user={session.currentUser} repositoryUrl={repo.configuration.repositoryUrl} />
</ListItemIcon>
<ListItemText
primaryTypographyProps={{
style: { overflow: 'hidden', textOverflow: 'ellipsis' },
title: session.currentUser.DisplayName || session.currentUser.Name,
}}
primary={`${session.currentUser.DisplayName || session.currentUser.Name} user`}
/>
</MenuItem>
<NavLink to="/personalSettings" onClick={handleClose}>
<MenuItem style={{ textDecoration: 'underline', color: theme.palette.primary.main }}>
{localization.topMenu.personalSettings}
</MenuItem>
</NavLink>
<MenuItem onClick={logout} style={{ textDecoration: 'underline', color: theme.palette.primary.main }}>
{localization.topMenu.logout}
</MenuItem>
<MenuItem>
<Typography component="div" style={{ color: theme.palette.primary.main }}>
<Grid component="label" container alignItems="center" spacing={1}>
<Grid item style={{ textTransform: 'uppercase', paddingRight: '32px' }}>
{personalSettings.theme === 'dark' ? 'Light theme' : 'Dark theme'}
</Grid>
<Grid item>
<AntSwitch checked={personalSettings.theme === 'dark'} onChange={switchTheme()} />
</Grid>
</Grid>
</Typography>
</MenuItem>
</MenuList>
</ClickAwayListener>
</Paper>
</Grow>
)}
</Popper>
</div>
)
}
102 changes: 13 additions & 89 deletions apps/sensenet/src/components/drawer/PermanentDrawer.tsx
Original file line number Diff line number Diff line change
@@ -1,43 +1,29 @@
import IconButton from '@material-ui/core/IconButton'
import List from '@material-ui/core/List'
import ListItem from '@material-ui/core/ListItem'
import ListItemIcon from '@material-ui/core/ListItemIcon'
import ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction'
import ListItemText from '@material-ui/core/ListItemText'
import Paper from '@material-ui/core/Paper'
import Tooltip from '@material-ui/core/Tooltip'
import KeyboardArrowLeft from '@material-ui/icons/KeyboardArrowLeft'
import KeyboardArrowRight from '@material-ui/icons/KeyboardArrowRight'
import Settings from '@material-ui/icons/Settings'
import { PathHelper } from '@sensenet/client-utils'
import { Close, Menu } from '@material-ui/icons'
import React, { useContext, useEffect, useState } from 'react'
import { withRouter } from 'react-router'
import { matchPath, NavLink, RouteComponentProps } from 'react-router-dom'
import { useRepository, useSession } from '@sensenet/hooks-react'
import { ResponsiveContext, ResponsivePersonalSetttings } from '../../context'
import { useDrawerItems, useLocalization, usePersonalSettings, useSelectionService, useTheme } from '../../hooks'
import { LogoutButton } from '../LogoutButton'
import { UserAvatar } from '../UserAvatar'
import { useRepository } from '@sensenet/hooks-react'
import { useDrawerItems, useLocalization, useSelectionService, useTheme } from '../../hooks'
import { ResponsivePersonalSetttings } from '../../context'
import { AddButton } from '../AddButton'

const PermanentDrawer: React.FunctionComponent<RouteComponentProps> = props => {
const settings = useContext(ResponsivePersonalSetttings)
const selectionService = useSelectionService()
const personalSettings = usePersonalSettings()
const theme = useTheme()
const session = useSession()
const repo = useRepository()
const device = useContext(ResponsiveContext)
const [currentComponent, setCurrentComponent] = useState(selectionService.activeContent.getValue())
const [currentPath, setCurrentPath] = useState('')
const [opened, setOpened] = useState(settings.drawer.type === 'permanent')
const items = useDrawerItems()
const localization = useLocalization().drawer

const [currentRepoEntry, setCurrentRepoEntry] = useState(
personalSettings.repositories.find(r => r.url === PathHelper.trimSlashes(repo.configuration.repositoryUrl)),
)

useEffect(() => {
const activeComponentObserve = selectionService.activeContent.subscribe(newActiveComponent =>
setCurrentComponent(newActiveComponent),
Expand All @@ -48,12 +34,6 @@ const PermanentDrawer: React.FunctionComponent<RouteComponentProps> = props => {
}
}, [selectionService.activeContent])

useEffect(() => {
setCurrentRepoEntry(
personalSettings.repositories.find(r => r.url === PathHelper.trimSlashes(repo.configuration.repositoryUrl)),
)
}, [personalSettings, repo])

if (!settings.drawer.enabled) {
return null
}
Expand All @@ -74,6 +54,15 @@ const PermanentDrawer: React.FunctionComponent<RouteComponentProps> = props => {
transition: 'width 100ms ease-in-out',
}}>
<div style={{ paddingTop: '1em', overflowY: 'auto', overflowX: 'hidden' }}>
<div>
{settings.drawer.type === 'mini-variant' ? (
<ListItem button={true} onClick={() => setOpened(!opened)} key="expandcollapse">
<Tooltip title={opened ? localization.collapse : localization.expand} placement="right">
<ListItemIcon>{opened ? <Close /> : <Menu />}</ListItemIcon>
</Tooltip>
</ListItem>
) : null}
</div>
<AddButton isOpened={opened} parent={currentComponent} path={currentPath} />
{items.map((item, index) => {
return (
Expand Down Expand Up @@ -101,71 +90,6 @@ const PermanentDrawer: React.FunctionComponent<RouteComponentProps> = props => {
)
})}
</div>
<div>
{opened ? (
<Paper style={{ padding: '1em' }}>
<ListItem>
<ListItemIcon>
<UserAvatar user={session.currentUser} repositoryUrl={repo.configuration.repositoryUrl} />
</ListItemIcon>
<ListItemText
primaryTypographyProps={{
style: { overflow: 'hidden', textOverflow: 'ellipsis' },
title: session.currentUser.DisplayName || session.currentUser.Name,
}}
secondaryTypographyProps={{
style: { overflow: 'hidden', textOverflow: 'ellipsis' },
title: (currentRepoEntry && currentRepoEntry.displayName) || repo.configuration.repositoryUrl,
}}
primary={session.currentUser.DisplayName || session.currentUser.Name}
secondary={(currentRepoEntry && currentRepoEntry.displayName) || repo.configuration.repositoryUrl}
/>
<ListItemSecondaryAction>
{device === 'mobile' ? null : (
<NavLink to="/personalSettings" style={{ textDecoration: 'none' }}>
<IconButton title={localization.personalSettingsTitle}>
<Settings />
</IconButton>
</NavLink>
)}
<LogoutButton />
</ListItemSecondaryAction>
</ListItem>
</Paper>
) : (
<>
<NavLink
to="/personalSettings"
style={{ textDecoration: 'none', opacity: 0.54 }}
key="personalSettings"
onClick={() => setCurrentPath('')}>
<ListItem button={true}>
<Tooltip
title={
<React.Fragment>
{localization.personalSettingsTitle} <br /> {localization.personalSettingsSecondaryText}
</React.Fragment>
}
placement="right">
<ListItemIcon>
<Settings />
</ListItemIcon>
</Tooltip>
</ListItem>
</NavLink>
<LogoutButton buttonStyle={{ width: '100%' }} />
</>
)}

{settings.drawer.type === 'mini-variant' ? (
<ListItem button={true} onClick={() => setOpened(!opened)} key="expandcollapse">
<Tooltip title={opened ? localization.collapse : localization.expand} placement="right">
<ListItemIcon>{opened ? <KeyboardArrowLeft /> : <KeyboardArrowRight />}</ListItemIcon>
</Tooltip>
{opened ? <ListItemText primary={localization.collapse} /> : null}
</ListItem>
) : null}
</div>
</List>
</Paper>
)
Expand Down
2 changes: 1 addition & 1 deletion apps/sensenet/src/components/theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import zIndex from '@material-ui/core/styles/zIndex'

const theme: ThemeOptions = {
palette: {
type: 'dark',
type: 'light',
secondary: {
light: '#90caf9',
main: '#1976d2',
Expand Down
Loading