@onextech/react-semantic-booster
Version:
Extended components for react-semantic-ui
278 lines (253 loc) • 7.85 kB
JavaScript
import React from 'react';
import styled from 'styled-components';
import PropTypes from 'prop-types';
import { kebabCase } from 'lodash';
import { Sidebar, Dropdown, Responsive, Dimmer, Image, Button } from 'semantic-ui-react';
import MenuLink from '../../atoms/MenuLink';
import DropdownLink from '../../atoms/DropdownLink';
import ButtonLink from '../../atoms/ButtonLink';
import Menu from '../../atoms/Menu';
import { mediaCssBreakpoints, MediaCss } from '../../../utils/responsive';
import { mergeClassNames } from '../../../utils/helpers';
const MENU_CENTER_CLASS = 'center';
const SidebarPushable = styled(Sidebar.Pushable)`
/* Button */
.ui.menu.vertical .ui.button {
width: 100%;
}
/* Fix for button padding in SUI menus */
.ui.menu:not(.vertical) .item>.button.custom {
padding-top: 0;
padding-bottom: 0;
}
/* Dropdown */
.ui.menu .ui.dropdown .menu>.item.fitted.link {
display: flex;
padding: 0 !important;
a {
color: inherit;
&:focus, &:hover {
text-decoration: none;
}
}
}
// Fix for dropdown link in menu
.inverted.ui.menu .ui.dropdown .menu > .item.fitted.link a {
color: rgba(0,0,0,.87);
}
// Fix for dropdown text alignment in vertical menu
.ui.menu.vertical .ui.item.dropdown {
text-align: left;
}
/* Fix for dropdown menu not showing in sidebar due to SUI */
.ui.sidebar {
overflow-y: initial !important;
}
/* Sidebar */
// Fix for menu items not positioned correctly
.ui.secondary.pointing.menu .item {
align-self: center;
}
// Fix for removing padding-left on hamburger icon in secondary menu only only
.ui.secondary.pointing.menu:not(.sidebar) {
.ui.item.dropdown[role="listbox"] {
padding-left: 0;
}
.right, .left {
.ui.item.dropdown[role="listbox"] {
padding-left: 1.14286em;
}
}
}
.ui.menu.${MENU_CENTER_CLASS} .container {
${MediaCss.min.sm`justify-content: center;`};
> .menu { display: flex; }
}
`;
class SiteNav extends React.Component {
state = {}
toggleSidebar = () => this.setState({ showSidebar: !this.state.showSidebar })
handleDimmerClick = () => this.setState({ showSidebar: false });
renderMenuItems = (items, options = {}) => {
const { isMobile = false, center = false } = options;
const result = [];
const defaultMenuLinkProps = {};
if (isMobile) {
defaultMenuLinkProps.onClick = this.toggleSidebar;
}
items.map((item) => {
const { to, name, image, button, dropdown, ...rest } = item;
const key = kebabCase(name);
let jsx;
switch (true) {
case Boolean(image): {
if (!center) {
jsx = (
<MenuLink
to={to}
key={key}
{...defaultMenuLinkProps}
{...rest}>
<Image {...item.image} />
</MenuLink>
);
}
break;
}
case Boolean(button): {
jsx = (
<Menu.Item key={key} {...rest}>
{
to ?
<ButtonLink
to={to}
content={name}
{...button}
{...defaultMenuLinkProps} /> :
<Button content={name} {...button} />
}
</Menu.Item>
);
break;
}
case Boolean(dropdown): {
jsx = (
<Dropdown item text={item.name} key={key} {...rest}>
<Dropdown.Menu>
{
dropdown.items.map(dropdownItem => (
<DropdownLink
to={dropdownItem.to}
key={kebabCase(dropdownItem.name)}
{...defaultMenuLinkProps}>
{dropdownItem.name}
</DropdownLink>
))
}
</Dropdown.Menu>
</Dropdown>
);
break;
}
default: {
jsx = (
<MenuLink
to={to || '/'}
key={key}
{...defaultMenuLinkProps}
{...rest}>
{name}
</MenuLink>
);
break;
}
}
return result.push(jsx);
});
return result;
};
renderDesktopMenu = () => {
const { menuProps, menu, menuTop } = this.props;
// Check for centered menu. There should only be 1 menu for centered menus. So take the first array index
const isCenteredMenu = menu.length === 1 && menu[0].position === 'center';
const extraMenuProps = {};
const renderMenuItemsOptions = {};
// Alter props with menu is centered
if (isCenteredMenu) {
renderMenuItemsOptions.center = true;
extraMenuProps.className = mergeClassNames(MENU_CENTER_CLASS, menuProps.className);
}
return (
<React.Fragment>
{menuTop}
{isCenteredMenu && menu[0].content.map((content, i) => {
const { image, to } = content;
if (image) {
return (
<Responsive key={i} minWidth={mediaCssBreakpoints.sm} style={{ display: 'flex', justifyContent: 'center' }}>
<MenuLink to={to}><Image {...image} /></MenuLink>
</Responsive>
);
}
})}
<Menu attached {...menuProps} {...extraMenuProps}>
<Responsive maxWidth={mediaCssBreakpoints.sm} as={Menu.Menu}>
<Dropdown item icon="content" onClick={this.toggleSidebar} />
</Responsive>
{menu.map((submenu, i) => (
<Responsive
key={i}
as={Menu.Menu}
minWidth={mediaCssBreakpoints.sm}
// Semantic Menu.Menu.position prop only takes in enums ['left', 'right']
position={submenu.position === 'center' ? null : submenu.position}
{...submenu.props}
>
{this.renderMenuItems(submenu.content, renderMenuItemsOptions)}
</Responsive>
))}
</Menu>
</React.Fragment>
);
};
renderMobileMenu = () => {
const { menu } = this.props;
const allMenus = [];
if (menu.length >= 1) {
// merge menu contents
menu.map(submenu => allMenus.push(...submenu.content));
}
return this.renderMenuItems(allMenus, { isMobile: true });
}
render() {
const { children } = this.props;
const { showSidebar } = this.state;
return (
<SidebarPushable>
<Sidebar
as={Menu}
animation='push'
width='thin'
visible={showSidebar}
icon='labeled'
vertical
inverted>
{this.renderMobileMenu()}
</Sidebar>
<Sidebar.Pusher>
{this.renderDesktopMenu()}
<Dimmer active={showSidebar} onClick={this.handleDimmerClick} />
{children}
</Sidebar.Pusher>
</SidebarPushable>
);
}
}
SiteNav.propTypes = {
menu: PropTypes.arrayOf(PropTypes.shape({
position: PropTypes.string.isRequired,
content: PropTypes.arrayOf(PropTypes.shape({
name: PropTypes.string.isRequired,
to: PropTypes.string,
dropdown: PropTypes.shape({
items: PropTypes.arrayOf(PropTypes.shape({
name: PropTypes.string.isRequired,
to: PropTypes.string.isRequired,
})).isRequired,
}),
button: PropTypes.object,
})).isRequired,
})).isRequired,
children: PropTypes.oneOfType([
PropTypes.element,
PropTypes.string,
PropTypes.array,
]),
menuProps: PropTypes.object,
menuTop: PropTypes.element,
};
SiteNav.defaultProps = {
menuProps: undefined,
menuTop: undefined,
};
export default SiteNav;