Skip to content
Snippets Groups Projects
TopBar.js 8.79 KiB
Newer Older
import React from 'react'
import PropTypes from 'prop-types'
import intl from 'react-intl-universal'
import AppBar from '@material-ui/core/AppBar'
import Toolbar from '@material-ui/core/Toolbar'
import IconButton from '@material-ui/core/IconButton'
esikkala's avatar
esikkala committed
import Typography from '@material-ui/core/Typography'
import MenuItem from '@material-ui/core/MenuItem'
import Menu from '@material-ui/core/Menu'
esikkala's avatar
esikkala committed
import { makeStyles } from '@material-ui/core/styles'
import MoreIcon from '@material-ui/icons/MoreVert'
import Button from '@material-ui/core/Button'
import { Link, NavLink } from 'react-router-dom'
import TopBarSearchField from './TopBarSearchField'
import TopBarInfoButton from './TopBarInfoButton'
esikkala's avatar
esikkala committed
import TopBarLanguageButton from './TopBarLanguageButton'
import Divider from '@material-ui/core/Divider'
import { has } from 'lodash'
esikkala's avatar
esikkala committed
import secoLogo from '../../img/logos/seco-logo-48x50.png'
esikkala's avatar
esikkala committed
import { showLanguageButton } from '../../configs/sampo/GeneralConfig'
esikkala's avatar
esikkala committed
const useStyles = makeStyles((theme) => ({
    flexGrow: 1
  toolbar: {
    paddingLeft: theme.spacing(1.5),
    paddingRight: theme.spacing(1.5)
  sectionDesktop: {
    display: 'none',
esikkala's avatar
esikkala committed
    [theme.breakpoints.up('lg')]: {
      display: 'flex'
    }
  link: {
    textDecoration: 'none'
  },
  sectionMobile: {
    display: 'flex',
esikkala's avatar
esikkala committed
    [theme.breakpoints.up('lg')]: {
      display: 'none'
    }
  homeButtonText: {
    whiteSpace: 'nowrap',
    [theme.breakpoints.down('sm')]: {
      fontSize: '1rem'
    }
  },
    whiteSpace: 'nowrap',
esikkala's avatar
esikkala committed
    color: 'white !important',
    border: `1px solid ${theme.palette.primary.main}`
  appBarButtonActive: {
    border: '1px solid white'
esikkala's avatar
esikkala committed
  },
  appBarDivider: {
    marginLeft: theme.spacing(1),
    marginRight: theme.spacing(1),
    borderLeft: '2px solid white'
  },
  secoLogo: {
    marginLeft: theme.spacing(1),
    [theme.breakpoints.down('md')]: {
      display: 'none'
    }
esikkala's avatar
esikkala committed
}))
esikkala's avatar
esikkala committed
const TopBar = props => {
  const [mobileMoreAnchorEl, setMobileMoreAnchorEl] = React.useState(null)
  const isMobileMenuOpen = Boolean(mobileMoreAnchorEl)
  const { perspectives, currentLocale, availableLocales, rootUrl } = props
  const classes = useStyles()
  const handleMobileMenuOpen = event => setMobileMoreAnchorEl(event.currentTarget)
  const handleMobileMenuClose = () => setMobileMoreAnchorEl(null)
  // https://material-ui.com/components/buttons/#third-party-routing-library
esikkala's avatar
esikkala committed
  const AdapterLink = React.forwardRef((props, ref) => <Link innerRef={ref} {...props} />)
  const AdapterNavLink = React.forwardRef((props, ref) => <NavLink innerRef={ref} {...props} />)
esikkala's avatar
esikkala committed
  const renderMobileMenuItem = perspective => {
    const searchMode = perspective.id.startsWith('clientFS') ? 'federated-search' : 'faceted-search'
    if (has(perspective, 'externalUrl')) {
esikkala's avatar
esikkala committed
          className={classes.link}
          key={perspective.id}
          href={perspective.externalUrl}
          target='_blank'
          rel='noopener noreferrer'
        >
          <MenuItem>
            {intl.get(`perspectives.${perspective.id}.label`).toUpperCase()}
          </MenuItem>
        </a>
    } else {
        <MenuItem
          key={perspective.id}
esikkala's avatar
esikkala committed
          component={AdapterLink}
          to={`${props.rootUrl}/${perspective.id}/${searchMode}`}
          {intl.get(`perspectives.${perspective.id}.label`).toUpperCase()}
        </MenuItem>
esikkala's avatar
esikkala committed
  const renderDesktopTopMenuItem = perspective => {
    const searchMode = perspective.id.startsWith('clientFS') ? 'federated-search' : 'faceted-search'
    if (has(perspective, 'externalUrl')) {
esikkala's avatar
esikkala committed
          className={classes.link}
          key={perspective.id}
          href={perspective.externalUrl}
          target='_blank'
          rel='noopener noreferrer'
        >
          <Button
esikkala's avatar
esikkala committed
            className={classes.appBarButton}
esikkala's avatar
esikkala committed
          >
            {intl.get(`perspectives.${perspective.id}.label`).toUpperCase()}
          </Button>
        </a>
    } else {
        <Button
          key={perspective.id}
esikkala's avatar
esikkala committed
          className={classes.appBarButton}
          component={AdapterNavLink}
          to={`${props.rootUrl}/${perspective.id}/${searchMode}`}
          isActive={(match, location) => location.pathname.startsWith(`${props.rootUrl}/${perspective.id}`)}
          activeClassName={classes.appBarButtonActive}
          {intl.get(`perspectives.${perspective.id}.label`).toUpperCase()}
        </Button>
esikkala's avatar
esikkala committed
  const renderMobileMenu = perspectives =>
    <Menu
esikkala's avatar
esikkala committed
      anchorEl={mobileMoreAnchorEl}
      anchorOrigin={{ vertical: 'top', horizontal: 'right' }}
      transformOrigin={{ vertical: 'top', horizontal: 'right' }}
esikkala's avatar
esikkala committed
      open={isMobileMenuOpen}
      onClose={handleMobileMenuClose}
esikkala's avatar
esikkala committed
      {perspectives.map(perspective => renderMobileMenuItem(perspective))}
esikkala's avatar
esikkala committed
      <Divider />
      <MenuItem
        key='feedback'
esikkala's avatar
esikkala committed
        component={AdapterLink}
        to={`${props.rootUrl}/feedback`}
esikkala's avatar
esikkala committed
        {intl.get('topBar.feedback').toUpperCase()}
esikkala's avatar
esikkala committed
      </MenuItem>
esikkala's avatar
esikkala committed
      <MenuItem
        key={0}
esikkala's avatar
esikkala committed
        component={AdapterLink}
        to={`${props.rootUrl}/about`}
esikkala's avatar
esikkala committed
      >
        {intl.get('topBar.info.aboutThePortal').toUpperCase()}
esikkala's avatar
esikkala committed
      </MenuItem>
esikkala's avatar
esikkala committed
        className={classes.link}
esikkala's avatar
esikkala committed
        key={1}
esikkala's avatar
esikkala committed
        href={intl.get('topBar.info.blogUrl')}
esikkala's avatar
esikkala committed
        target='_blank'
        rel='noopener noreferrer'
      >
        <MenuItem>
esikkala's avatar
esikkala committed
          {intl.get('topBar.info.blog').toUpperCase()}
esikkala's avatar
esikkala committed
        </MenuItem>
      </a>
esikkala's avatar
esikkala committed
      <MenuItem
        key='info'
esikkala's avatar
esikkala committed
        component={AdapterLink}
        to={`${props.rootUrl}/instructions`}
esikkala's avatar
esikkala committed
        {intl.get('topBar.instructions').toUpperCase()}
esikkala's avatar
esikkala committed
      </MenuItem>
    </Menu>

esikkala's avatar
esikkala committed
  return (
    <div className={classes.root}>
      {/* Add an empty Typography element to ensure that that the MuiTypography class is loaded for
esikkala's avatar
esikkala committed
         any lower level components that use MuiTypography class only in translation files */}
esikkala's avatar
esikkala committed
      <Typography />
      <AppBar position='absolute'>
        <Toolbar className={classes.toolbar}>
          <Button component={AdapterLink} to='/'>
            <Typography className={classes.homeButtonText} variant='h6'>{intl.get('appTitle.short')}</Typography>
          </Button>
          <TopBarSearchField
            fetchFullTextResults={props.fetchFullTextResults}
            clearResults={props.clearResults}
            xsScreen={props.xsScreen}
            rootUrl={rootUrl}
          />
          <div className={classes.grow} />
          <div className={classes.sectionDesktop}>
            {perspectives.map((perspective, index) => renderDesktopTopMenuItem(perspective, index))}
            <div className={classes.appBarDivider} />
            <Button
              className={classes.appBarButton}
              component={AdapterNavLink}
              to={`${props.rootUrl}/feedback`}
              isActive={(match, location) => location.pathname.startsWith(`${props.rootUrl}/feedback`)}
              activeClassName={classes.appBarButtonActive}
            >
              {intl.get('topBar.feedback')}
esikkala's avatar
esikkala committed
            <TopBarInfoButton rootUrl={props.rootUrl} />
            <Button
              className={classes.appBarButton}
              component={AdapterNavLink}
              to={`${props.rootUrl}/instructions`}
              isActive={(match, location) => location.pathname.startsWith(`${props.rootUrl}/instructions`)}
              activeClassName={classes.appBarButtonActive}
esikkala's avatar
esikkala committed
              {intl.get('topBar.instructions')}
            </Button>
            {showLanguageButton &&
              <TopBarLanguageButton
                currentLocale={currentLocale}
                availableLocales={availableLocales}
                loadLocales={props.loadLocales}
                location={props.location}
              />}
          </div>
          <a
            className={classes.secoLogo}
            href='https://seco.cs.aalto.fi'
            target='_blank'
            rel='noopener noreferrer'
          >
            <Button><img src={secoLogo} /></Button>
          </a>
          <div className={classes.sectionMobile}>
            <IconButton aria-haspopup='true' onClick={handleMobileMenuOpen} color='inherit'>
              <MoreIcon />
            </IconButton>
          </div>
        </Toolbar>
      </AppBar>
      {renderMobileMenu(perspectives)}
    </div>
  )
}

TopBar.propTypes = {
  fetchFullTextResults: PropTypes.func.isRequired,
  clearResults: PropTypes.func.isRequired,
  loadLocales: PropTypes.func.isRequired,
  perspectives: PropTypes.array.isRequired,
  currentLocale: PropTypes.string.isRequired,
  availableLocales: PropTypes.array.isRequired,
esikkala's avatar
esikkala committed
  xsScreen: PropTypes.bool.isRequired,
  location: PropTypes.object.isRequired,
esikkala's avatar
esikkala committed
  /**
   * Root url of the application.
   */
  rootUrl: PropTypes.string.isRequired
esikkala's avatar
esikkala committed
export default TopBar