Skip to content
Snippets Groups Projects
TopBar.js 8.75 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'
import { withStyles } 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'

const styles = 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'
    }
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'
    }

class TopBar extends React.Component {
  state = {
esikkala's avatar
esikkala committed
    infoAnchorEl: null,
    mobileMoreAnchorEl: null
  handleInfoMenuOpen = event => {
    this.setState({ infoAnchorEl: event.currentTarget })
esikkala's avatar
esikkala committed
  handleInfoMenuClose = () => {
    this.setState({ infoAnchorEl: null })
  handleMobileMenuOpen = event => {
    this.setState({ mobileMoreAnchorEl: event.currentTarget })
  handleMobileMenuClose = () => {
    this.setState({ mobileMoreAnchorEl: null })
  // https://material-ui.com/components/buttons/#third-party-routing-library
  AdapterLink = React.forwardRef((props, ref) => <Link innerRef={ref} {...props} />);
  AdapterNavLink = React.forwardRef((props, ref) => <NavLink innerRef={ref} {...props} />);
  renderMobileMenuItem = perspective => {
    const searchMode = perspective.id.startsWith('clientFS') ? 'federated-search' : 'faceted-search'
    if (has(perspective, 'externalUrl')) {
      return (
        <a
          className={this.props.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}
          component={this.AdapterLink}
          to={`/${perspective.id}/${searchMode}`}
          {intl.get(`perspectives.${perspective.id}.label`).toUpperCase()}
        </MenuItem>
  renderDesktopTopMenuItem = perspective => {
    const searchMode = perspective.id.startsWith('clientFS') ? 'federated-search' : 'faceted-search'
    if (has(perspective, 'externalUrl')) {
      return (
        <a
          className={this.props.classes.link}
          key={perspective.id}
          href={perspective.externalUrl}
          target='_blank'
          rel='noopener noreferrer'
        >
          <Button
            className={this.props.classes.appBarButton}
esikkala's avatar
esikkala committed
          >
            {intl.get(`perspectives.${perspective.id}.label`).toUpperCase()}
          </Button>
        </a>
    } else {
        <Button
          key={perspective.id}
          className={this.props.classes.appBarButton}
          component={this.AdapterNavLink}
          to={`/${perspective.id}/${searchMode}`}
          isActive={(match, location) => location.pathname.startsWith(`/${perspective.id}`)}
          activeClassName={this.props.classes.appBarButtonActive}
        >
          {intl.get(`perspectives.${perspective.id}.label`).toUpperCase()}
        </Button>
  renderMobileMenu = perspectives =>
    <Menu
      anchorEl={this.state.mobileMoreAnchorEl}
      anchorOrigin={{ vertical: 'top', horizontal: 'right' }}
      transformOrigin={{ vertical: 'top', horizontal: 'right' }}
      open={Boolean(this.state.mobileMoreAnchorEl)}
      onClose={this.handleMobileMenuClose}
    >
      {perspectives.map(perspective => this.renderMobileMenuItem(perspective))}
esikkala's avatar
esikkala committed
      <Divider />
      <MenuItem
        key='feedback'
        component={this.AdapterLink}
        to='/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}
        component={this.AdapterLink}
esikkala's avatar
esikkala committed
      >
        {intl.get('topBar.info.aboutThePortal').toUpperCase()}
esikkala's avatar
esikkala committed
      </MenuItem>
      <a
        className={this.props.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'
        component={this.AdapterLink}
        to='/instructions'
esikkala's avatar
esikkala committed
        {intl.get('topBar.instructions').toUpperCase()}
esikkala's avatar
esikkala committed
      </MenuItem>
    </Menu>

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

TopBar.propTypes = {
  classes: PropTypes.object.isRequired,
  fetchResultsClientSide: PropTypes.func.isRequired,
  clearResults: PropTypes.func.isRequired,
  loadLocales: PropTypes.func.isRequired,
  perspectives: PropTypes.array.isRequired,
  currentLocale: PropTypes.string.isRequired,
  availableLocales: PropTypes.array.isRequired,
  xsScreen: PropTypes.bool.isRequired
export default withStyles(styles)(TopBar)