2019-06-20 19:37:28 +07:00
|
|
|
import React, { SyntheticEvent, Component, Fragment, ReactElement } from 'react';
|
2019-02-03 17:23:33 +07:00
|
|
|
import { Link } from 'react-router-dom';
|
2019-06-30 05:39:56 +07:00
|
|
|
import { css } from 'emotion';
|
2019-02-03 17:23:33 +07:00
|
|
|
|
2019-06-20 19:37:28 +07:00
|
|
|
import Button from '@material-ui/core/Button';
|
|
|
|
import IconButton from '@material-ui/core/IconButton';
|
|
|
|
import MenuItem from '@material-ui/core/MenuItem';
|
|
|
|
import Menu from '@material-ui/core/Menu';
|
2019-02-03 17:23:33 +07:00
|
|
|
import Info from '@material-ui/icons/Info';
|
|
|
|
import Help from '@material-ui/icons/Help';
|
2019-06-20 19:37:28 +07:00
|
|
|
import Tooltip from '@material-ui/core/Tooltip';
|
2019-02-03 17:23:33 +07:00
|
|
|
import AccountCircle from '@material-ui/icons/AccountCircle';
|
|
|
|
import { default as IconSearch } from '@material-ui/icons/Search';
|
|
|
|
|
|
|
|
import { getRegistryURL } from '../../utils/url';
|
|
|
|
import ExternalLink from '../Link';
|
|
|
|
import Logo from '../Logo';
|
2019-06-20 19:37:28 +07:00
|
|
|
import RegistryInfoDialog from '../RegistryInfoDialog/RegistryInfoDialog';
|
|
|
|
import Label from '../Label/Label';
|
|
|
|
import Search from '../Search/Search';
|
|
|
|
import RegistryInfoContent from '../RegistryInfoContent/RegistryInfoContent';
|
2019-02-03 17:23:33 +07:00
|
|
|
|
|
|
|
import { Greetings, NavBar, InnerNavBar, MobileNavBar, InnerMobileNavBar, LeftSide, RightSide, IconSearchButton, SearchWrapper } from './styles';
|
|
|
|
|
2019-06-20 19:37:28 +07:00
|
|
|
interface Props {
|
2019-06-26 06:10:15 +07:00
|
|
|
logo?: string;
|
2019-06-20 19:37:28 +07:00
|
|
|
username?: string;
|
|
|
|
onLogout: () => void;
|
|
|
|
onToggleLoginModal: () => void;
|
|
|
|
scope: string;
|
|
|
|
withoutSearch?: boolean;
|
|
|
|
}
|
|
|
|
|
|
|
|
interface State {
|
2019-08-31 16:02:46 +07:00
|
|
|
anchorEl?: null | Element | ((element: Element) => Element);
|
2019-06-20 19:37:28 +07:00
|
|
|
openInfoDialog: boolean;
|
|
|
|
registryUrl: string;
|
|
|
|
showMobileNavBar: boolean;
|
|
|
|
}
|
|
|
|
|
|
|
|
type ToolTipType = 'search' | 'help' | 'info';
|
2019-02-03 17:23:33 +07:00
|
|
|
|
2019-06-20 19:37:28 +07:00
|
|
|
class Header extends Component<Props, State> {
|
|
|
|
constructor(props: Props) {
|
2019-02-03 17:23:33 +07:00
|
|
|
super(props);
|
|
|
|
this.state = {
|
|
|
|
openInfoDialog: false,
|
|
|
|
registryUrl: getRegistryURL(),
|
|
|
|
showMobileNavBar: false,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2019-06-20 19:37:28 +07:00
|
|
|
public render(): ReactElement<HTMLElement> {
|
|
|
|
const { showMobileNavBar } = this.state;
|
|
|
|
const { withoutSearch = false } = this.props;
|
|
|
|
return (
|
|
|
|
<div>
|
|
|
|
<NavBar position="static">
|
|
|
|
<InnerNavBar>
|
|
|
|
{this.renderLeftSide()}
|
|
|
|
{this.renderRightSide()}
|
|
|
|
</InnerNavBar>
|
|
|
|
{this.renderInfoDialog()}
|
|
|
|
</NavBar>
|
|
|
|
{showMobileNavBar && !withoutSearch && (
|
|
|
|
<MobileNavBar>
|
|
|
|
<InnerMobileNavBar>
|
|
|
|
<Search />
|
|
|
|
</InnerMobileNavBar>
|
|
|
|
<Button color="inherit" onClick={this.handleDismissMNav}>
|
|
|
|
{'Cancel'}
|
|
|
|
</Button>
|
|
|
|
</MobileNavBar>
|
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2019-02-03 17:23:33 +07:00
|
|
|
/**
|
|
|
|
* opens popover menu for logged in user.
|
|
|
|
*/
|
2019-06-20 19:37:28 +07:00
|
|
|
public handleLoggedInMenu = (event: SyntheticEvent<HTMLElement>) => {
|
2019-02-03 17:23:33 +07:00
|
|
|
this.setState({
|
|
|
|
anchorEl: event.currentTarget,
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* closes popover menu for logged in user
|
|
|
|
*/
|
2019-06-20 19:37:28 +07:00
|
|
|
public handleLoggedInMenuClose = () => {
|
2019-02-03 17:23:33 +07:00
|
|
|
this.setState({
|
|
|
|
anchorEl: null,
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* opens registry information dialog.
|
|
|
|
*/
|
2019-06-20 19:37:28 +07:00
|
|
|
public handleOpenRegistryInfoDialog = () => {
|
2019-02-03 17:23:33 +07:00
|
|
|
this.setState({
|
|
|
|
openInfoDialog: true,
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* closes registry information dialog.
|
|
|
|
*/
|
2019-06-20 19:37:28 +07:00
|
|
|
public handleCloseRegistryInfoDialog = () => {
|
2019-02-03 17:23:33 +07:00
|
|
|
this.setState({
|
|
|
|
openInfoDialog: false,
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* close/open popover menu for logged in users.
|
|
|
|
*/
|
2019-06-20 19:37:28 +07:00
|
|
|
public handleToggleLogin = () => {
|
2019-02-03 17:23:33 +07:00
|
|
|
const { onToggleLoginModal } = this.props;
|
|
|
|
this.setState(
|
|
|
|
{
|
|
|
|
anchorEl: null,
|
|
|
|
},
|
|
|
|
onToggleLoginModal
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
2019-06-20 19:37:28 +07:00
|
|
|
public handleToggleMNav = () => {
|
2019-02-03 17:23:33 +07:00
|
|
|
const { showMobileNavBar } = this.state;
|
|
|
|
this.setState({
|
|
|
|
showMobileNavBar: !showMobileNavBar,
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2019-06-20 19:37:28 +07:00
|
|
|
public handleDismissMNav = () => {
|
2019-02-03 17:23:33 +07:00
|
|
|
this.setState({
|
|
|
|
showMobileNavBar: false,
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2019-06-20 19:37:28 +07:00
|
|
|
public renderLeftSide = () => {
|
2019-02-03 17:23:33 +07:00
|
|
|
const { withoutSearch = false } = this.props;
|
|
|
|
return (
|
|
|
|
<LeftSide>
|
2019-06-30 05:39:56 +07:00
|
|
|
<Link
|
|
|
|
className={css`
|
2019-07-06 05:58:18 +07:00
|
|
|
margin-right: 1em;
|
2019-06-30 05:39:56 +07:00
|
|
|
`}
|
|
|
|
to={'/'}>
|
2019-02-03 17:23:33 +07:00
|
|
|
{this.renderLogo()}
|
|
|
|
</Link>
|
|
|
|
{!withoutSearch && (
|
|
|
|
<SearchWrapper>
|
|
|
|
<Search />
|
|
|
|
</SearchWrapper>
|
|
|
|
)}
|
|
|
|
</LeftSide>
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
2019-06-20 19:37:28 +07:00
|
|
|
public renderLogo = () => {
|
2019-02-03 17:23:33 +07:00
|
|
|
const { logo } = this.props;
|
|
|
|
|
2019-06-20 19:37:28 +07:00
|
|
|
if (logo) {
|
|
|
|
return <img alt="logo" height="40px" src={logo} />;
|
2019-02-03 17:23:33 +07:00
|
|
|
} else {
|
|
|
|
return <Logo />;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-06-20 19:37:28 +07:00
|
|
|
public renderToolTipIcon = (title: string, type: ToolTipType) => {
|
2019-02-03 17:23:33 +07:00
|
|
|
let content;
|
|
|
|
switch (type) {
|
|
|
|
case 'help':
|
|
|
|
content = (
|
2019-06-20 19:37:28 +07:00
|
|
|
// @ts-ignore
|
2019-02-03 17:23:33 +07:00
|
|
|
<IconButton blank={true} color={'inherit'} component={ExternalLink} to={'https://verdaccio.org/docs/en/installation'}>
|
|
|
|
<Help />
|
|
|
|
</IconButton>
|
|
|
|
);
|
|
|
|
break;
|
|
|
|
case 'info':
|
|
|
|
content = (
|
2019-06-20 19:37:28 +07:00
|
|
|
<IconButton color="inherit" id="header--button-registryInfo" onClick={this.handleOpenRegistryInfoDialog}>
|
2019-02-03 17:23:33 +07:00
|
|
|
<Info />
|
|
|
|
</IconButton>
|
|
|
|
);
|
|
|
|
break;
|
|
|
|
case 'search':
|
|
|
|
content = (
|
2019-06-20 19:37:28 +07:00
|
|
|
<IconSearchButton color="inherit" onClick={this.handleToggleMNav}>
|
2019-02-03 17:23:33 +07:00
|
|
|
<IconSearch />
|
|
|
|
</IconSearchButton>
|
|
|
|
);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return (
|
|
|
|
<Tooltip disableFocusListener={true} title={title}>
|
|
|
|
{content}
|
|
|
|
</Tooltip>
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
2019-06-20 19:37:28 +07:00
|
|
|
public renderRightSide = () => {
|
2019-02-03 17:23:33 +07:00
|
|
|
const { username = '', withoutSearch = false } = this.props;
|
|
|
|
return (
|
|
|
|
<RightSide>
|
|
|
|
{!withoutSearch && this.renderToolTipIcon('Search packages', 'search')}
|
|
|
|
{this.renderToolTipIcon('Documentation', 'help')}
|
|
|
|
{this.renderToolTipIcon('Registry Information', 'info')}
|
|
|
|
{username ? (
|
|
|
|
this.renderMenu()
|
|
|
|
) : (
|
2019-06-20 19:37:28 +07:00
|
|
|
<Button color="inherit" id="header--button-login" onClick={this.handleToggleLogin}>
|
2019-02-03 17:23:33 +07:00
|
|
|
{'Login'}
|
|
|
|
</Button>
|
|
|
|
)}
|
|
|
|
</RightSide>
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
2019-06-20 19:37:28 +07:00
|
|
|
private renderGreetings = () => {
|
2019-02-03 17:23:33 +07:00
|
|
|
const { username = '' } = this.props;
|
|
|
|
return (
|
2019-06-20 19:37:28 +07:00
|
|
|
<Fragment>
|
|
|
|
<Greetings>{'Hi,'}</Greetings>
|
|
|
|
<Label capitalize={true} text={username} weight="bold" />
|
|
|
|
</Fragment>
|
2019-02-03 17:23:33 +07:00
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* render popover menu
|
|
|
|
*/
|
2019-06-20 19:37:28 +07:00
|
|
|
private renderMenu = () => {
|
2019-02-03 17:23:33 +07:00
|
|
|
const { onLogout } = this.props;
|
|
|
|
const { anchorEl } = this.state;
|
|
|
|
const open = Boolean(anchorEl);
|
|
|
|
return (
|
2019-06-20 19:37:28 +07:00
|
|
|
<>
|
|
|
|
<IconButton color="inherit" id="header--button-account" onClick={this.handleLoggedInMenu}>
|
2019-02-03 17:23:33 +07:00
|
|
|
<AccountCircle />
|
|
|
|
</IconButton>
|
|
|
|
<Menu
|
|
|
|
anchorEl={anchorEl}
|
|
|
|
anchorOrigin={{
|
|
|
|
vertical: 'top',
|
|
|
|
horizontal: 'right',
|
|
|
|
}}
|
2019-06-20 19:37:28 +07:00
|
|
|
id="sidebar-menu"
|
2019-02-03 17:23:33 +07:00
|
|
|
onClose={this.handleLoggedInMenuClose}
|
|
|
|
open={open}
|
|
|
|
transformOrigin={{
|
|
|
|
vertical: 'top',
|
|
|
|
horizontal: 'right',
|
|
|
|
}}>
|
|
|
|
<MenuItem disabled={true}>{this.renderGreetings()}</MenuItem>
|
2019-06-20 19:37:28 +07:00
|
|
|
<MenuItem id="header--button-logout" onClick={onLogout}>
|
2019-02-03 17:23:33 +07:00
|
|
|
{'Logout'}
|
|
|
|
</MenuItem>
|
|
|
|
</Menu>
|
2019-06-20 19:37:28 +07:00
|
|
|
</>
|
2019-02-03 17:23:33 +07:00
|
|
|
);
|
|
|
|
};
|
|
|
|
|
2019-06-20 19:37:28 +07:00
|
|
|
private renderInfoDialog = () => {
|
2019-02-03 17:23:33 +07:00
|
|
|
const { scope } = this.props;
|
|
|
|
const { openInfoDialog, registryUrl } = this.state;
|
|
|
|
return (
|
|
|
|
<RegistryInfoDialog onClose={this.handleCloseRegistryInfoDialog} open={openInfoDialog}>
|
|
|
|
<RegistryInfoContent registryUrl={registryUrl} scope={scope} />
|
|
|
|
</RegistryInfoDialog>
|
|
|
|
);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
export default Header;
|