@onextech/react-semantic-booster
Version:
Extended components for react-semantic-ui
228 lines (213 loc) • 6.09 kB
JavaScript
import React from 'react';
import PropTypes from 'prop-types';
import { Responsive, Menu, Button, Icon } from 'semantic-ui-react';
import styled from 'styled-components';
import { getCustomClassName, subtractObject } from '../../../utils/helpers';
import { mediaCssBreakpoints } from '../../../utils/responsive';
const visibleClassName = 'visible';
const containerClassName = 'container';
const sidebarClassName = 'sidebar';
const contentClassName = 'content';
const mobileClassName = 'mobile';
const menuClassName = 'toggle-menu';
const navScrollClassName = 'nav-scroll';
const Wrapper = styled.div`
& {
height: 100%;
display: flex;
flex-direction: column;
min-height: 100vh;
.${menuClassName} {
a.item { height: 100% }
}
.${containerClassName} {
width: 100%;
height: 100%;
display: flex;
flex: 1;
position: relative;
overflow: hidden;
> .${sidebarClassName} {
width: 0;
position: relative;
float: left;
top: 0;
left: 0;
max-height: 100%;
overflow: auto;
transition: width .3s ease-out;
}
> .${contentClassName} {
width: 100%;
position: relative;
float: right;
top: 0;
right: 0;
transition: width .3s ease-out;
}
}
// Nav Scroll
&.${navScrollClassName} .${menuClassName} {
overflow-x: auto;
}
// Visible
&.${visibleClassName} .${containerClassName} {
> .${sidebarClassName} {
${props => props.sidebarProps.percentageWidth && `width: ${props.sidebarProps.percentageWidth}%;`}
${props => props.sidebarProps.maxWidth && `max-width: ${props.sidebarProps.maxWidth};`}
position: initial;
transition: width .3s ease-out;
}
> .${contentClassName} {
${props => props.sidebarProps.percentageWidth && `width: calc(100% - ${props.sidebarProps.percentageWidth}%);`}
${props => props.sidebarProps.maxWidth && `min-width: calc(100% - ${props.sidebarProps.maxWidth});`}
transition: width .3s ease-out;
}
}
// Mobile
&.${mobileClassName} {
.${sidebarClassName} {
position: absolute;
z-index: 1;
background-color: white;
box-shadow: 10px 0px 17px -8px rgba(0,0,0,0.25);
}
&.${visibleClassName} {
.${sidebarClassName} {
width: 50%;
}
}
}
}
`;
class Sider extends React.Component {
state = {
visible: true,
mobile: false,
}
toggleVisibility = () => {
const state = { visible: !this.state.visible };
this.setState(state);
}
handleDesktopResize = (e, { width, minWidth }) => {
if (width < minWidth) {
this.setState({ visible: false, mobile: true });
}
}
handleMobileResize = (e, { width, maxWidth }) => {
if (width > maxWidth) {
this.setState({ visible: true, mobile: false });
}
}
render() {
const { visible, mobile } = this.state;
const {
sidebar,
children,
menuItems,
sidebarProps,
toggleProps,
showDesktopToggle,
...props
} = this.props;
// 1. Define custom props for this component
const classProps = {
visible: visibleClassName,
mobile: mobileClassName,
navScroll: navScrollClassName,
};
// 1.5. Define default props
const defaultProps = {
visible,
mobile,
showDesktopToggle: true,
navScroll: false,
};
const customProps = {
visible,
mobile,
sidebarProps,
};
const allProps = { ...defaultProps, ...props };
// 2. Render the custom class names
const className = props &&
getCustomClassName(classProps, allProps);
// 3. Clean up custom props from main prop set to avoid prop clashing
const semanticProps = props &&
subtractObject({ ...defaultProps, ...classProps }, allProps);
return (
<Wrapper {...semanticProps} {...customProps} className={className}>
<Menu attached className={menuClassName}>
<Responsive
maxWidth={mediaCssBreakpoints.sm}
fireOnMount
onUpdate={this.handleMobileResize}>
<Menu.Item onClick={this.toggleVisibility}>
<Icon name={toggleProps.icon} />
{toggleProps.mobileName || toggleProps.name}
</Menu.Item>
</Responsive>
<Responsive
minWidth={mediaCssBreakpoints.sm}
fireOnMount
onUpdate={this.handleDesktopResize}>
{
showDesktopToggle &&
<Menu.Item>
<Button
content={toggleProps.name}
icon={{ name: toggleProps.icon }}
onClick={this.toggleVisibility}
{...toggleProps.button}/>
</Menu.Item>
}
</Responsive>
{menuItems}
</Menu>
<div className={containerClassName}>
<div className={sidebarClassName}>{sidebar}</div>
<div className={contentClassName}>{children}</div>
</div>
</Wrapper>
);
}
}
Sider.propTypes = {
children: PropTypes.oneOfType([
PropTypes.element,
PropTypes.string,
PropTypes.array,
]).isRequired,
sidebar: PropTypes.element.isRequired,
sidebarProps: PropTypes.shape({
percentageWidth: PropTypes.number, // in percentage only e.g. 20, 30
maxWidth: PropTypes.string,
}),
showDesktopToggle: PropTypes.bool,
toggleProps: PropTypes.shape({
name: PropTypes.string,
mobileName: PropTypes.string,
icon: PropTypes.string,
button: PropTypes.object, // sui button props
}),
menuItems: PropTypes.oneOfType([
PropTypes.element,
PropTypes.array,
]),
};
Sider.defaultProps = {
sidebarProps: {
percentageWidth: 25,
maxWidth: '200px',
},
showDesktopToggle: true,
toggleProps: {
name: 'Toggle Navigation',
nameMobile: '',
icon: 'content',
button: {
primary: false,
},
},
};
export default Sider;