predix-ui
Version:
px-* web components as React styled components
320 lines (281 loc) • 8.48 kB
JavaScript
import React from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import styled, { css } from 'styled-components';
import NavItem from './px-app-nav-item';
import AppNavGroup from './px-app-nav-sub-group';
const AppNav = styled.div`
height: var(--px-app-nav-height, 4rem);
background-color: var(--px-app-nav-background-color, whitesmoke);
box-shadow: var(--px-app-nav-box-shadow, 0px 2px 4px var(--px-shadow-navigation, rgba(0, 0, 0, 0.2)));
display: block;
width: 100%;
text-size-adjust: 100%;
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
${props => props.vertical && css`
overflow-x: hidden;
overflow-y: hidden;
position: var(--px-app-nav-vertical-position, absolute);
height: var(--px-app-nav-vertical-height, 100%);
left: 0;
top: 0;
min-width: var(--px-app-nav-vertical-width, 4rem);
max-width: var(--px-app-nav-vertical-width, 4rem);
transition: var(--px-app-nav-vertical-transition, max-width 250ms ease);
${props.verticalOpened && css`
overflow-y: auto;
max-width: var(--px-app-nav-vertical-width--opened, 21.33333rem);
`}
`}
`;
const AppNavItems = styled.div`
flex: 1 1 auto;
display: flex;
max-width: 100%;
${props => props.vertical && css`
flex-direction: column;
height: 100%;
`}
`;
/**
* AppNav component
*/
class AppNavComponent extends React.Component {
constructor(props) {
super(props, { displayName: 'AppNav' });
this.state = {
selected: props.selected || 0,
onlyShowIcon: props.onlyShowIcon || props.vertical,
/* selectedIndex: props.selectedIndex || null,
collapseOpened: props.collapseOpened || false,
visibleItems: props.visibleItems,
collapseWithIcon: props.collapseWithIcon || false,
opened: props.opened || false,
collapseAll: props.collapseAll,
collapseAt: props.collapseAt,
propForSelect: props.propForSelect, */
vertical: props.vertical || false,
verticalOpened: props.verticalOpened || !props.vertical
};
this._items = [];
this._keys = [];
this._handleRef = this._handleRef.bind(this);
this._handleMouseEnter = this._handleMouseEnter.bind(this);
this._handleMouseExit = this._handleMouseExit.bind(this);
}
componentDidMount() {
if (this.base && this.vertical) {
this.base.addEventListener('mouseleave', this._handleMouseExit);
this.base.addEventListener('mouseenter', this._handleMouseEnter);
}
// this.handleClick(this.props.selected, this._getItemFromValue(this.props.selected));
}
componentWillReceiveProps(nextProps) {
if (this.state.selected !== nextProps.selected) {
this.setState(nextProps);
}
this.setState(nextProps);
}
componentWillMount() {
if (this.props.items) {
this.props.items.map((item, index) => {
this._keys.push(this.props.propForSelect ? item[this.props.propForSelect] : index);
this._items.push(item);
return item;
});
}
}
componentWillUnmount() {
if (this.base && this.vertical) {
this.base.removeEventListener('mouseleave', this._handleMouseExit);
this.base.removeEventListener('mouseenter', this._handleMouseEnter);
}
}
_handleRef(el) {
this.base = el;
}
_handleMouseEnter() {
if (!this.state.vertical) {
return;
}
this._mouseIsOverNav = true;
if (this._mouseIsOverNav && !this.state.verticalOpened) {
this._setVerticalOpened(true);
}
}
_handleMouseExit() {
if (!this.state.vertical) {
return;
}
this._mouseIsOverNav = false;
if (!this._mouseIsOverNav && this.state.verticalOpened) {
this._setVerticalOpened(false);
}
}
_setVerticalOpened(bool) {
this.setState({
verticalOpened: bool,
onlyShowIcon: !bool
});
}
_getIndexForValue(val) {
return this._keys.indexOf(val);
}
_getValueForIndex(index) {
return this._items[index];
}
_getItemFromValue(index) {
return this._getValueForIndex(this._getIndexForValue(index));
}
handleClick(val, child, isSubItem) {
const c = child;
const propForSelect = (this.props.propForSelect ? child[this.props.propForSelect] : val);
const index = (this.props.propForSelect ? child[this.props.propForSelect] : this._getIndexForValue(propForSelect));/* eslint-disable-line */
let item = this._getValueForIndex(index); /* eslint-disable-line */
if (c && c.children && !isSubItem) {
c.selected = true;
// console.warn('Item has children, do not set active');
return;
}
if (isSubItem) {
item = val;
}
const state = {
selected: propForSelect,
selectedIndex: this._getIndexForValue(propForSelect),
selectedItem: child
};
this.setState(state);
if (this.props.onChange) {
this.props.onChange(state);
}
}
_reset() {
this._items = [];
this._keys = [];
}
_getItemFromPropForSelect(value) {
const item = this.props.items.filter(val => val[this.props.propForSelect] === value);
return item ? item[0] : null;
}
_renderItem(child, index) {
const propForSelect = (this.props.propForSelect ? child[this.props.propForSelect] : index);
this._keys.push(propForSelect);
this._items.push(child);
const selected = (this.state.selected === this._getIndexForValue(propForSelect));
if (!child.children) {
return (
<NavItem
key={index}
item={child}
id={child.id}
icon={child.icon}
label={child.label}
selected={selected}
onlyShowIcon={this.state.onlyShowIcon}
onClick={this.handleClick.bind(this, propForSelect, child)} /* eslint-disable-line */
/>
);
}
return (
<AppNavGroup
key={index}
id={child.id}
item={child}
icon={child.icon}
label={child.label}
onlyShowIcon={this.state.onlyShowIcon}
selected={selected}
onClick={this.handleClick.bind(this, propForSelect, child)} /* eslint-disable-line */
/>
);
}
_renderItems(items) {
this._reset();
return items.map(this._renderItem.bind(this));
}
render() {
const {
items,
children,
collapseAll,
collapseAt,
opened,
selectedIndex,
collapseWithIcon,
collapseOpened
} = this.props;
const {
vertical,
verticalOpened
} = this.state;
const baseClasses = classnames(
'px-app-nav',
{ vertical },
{ 'vertical-opened': vertical && verticalOpened }
);
return (
<AppNav
className={baseClasses}
ref={this._handleRef}
vertical={vertical}
collapseAll={collapseAll}
collapseWithIcon={collapseWithIcon}
selectedIndex={selectedIndex}
collapseAt={collapseAt}
collapseOpened={collapseOpened}
verticalOpened={verticalOpened}
>
<AppNavItems vertical={vertical} opened={opened}>
{this._renderItems(items, selectedIndex)}
</AppNavItems>
{/* STATE: Horizontal or menu nav, any visible items */}
{/* STATE: Items overflowed or collapsed */}
{/* Actions */}
{children}
</AppNav>
);
}
}
AppNavComponent.defaultProps = {
vertical: false,
collapseAll: false,
collapseAt: null,
collapseWithIcon: false,
onlyShowIcon: false,
collapseOpened: false,
verticalOpened: false,
selected: 0,
selectedItem: null,
selectedIndex: null,
visibleItems: null,
items: null,
propForSelect: null,
children: null,
onChange: null,
opened: null
};
AppNavComponent.propTypes = {
onChange: PropTypes.func,
vertical: PropTypes.bool,
collapseAll: PropTypes.bool,
collapseAt: PropTypes.number,
collapseWithIcon: PropTypes.bool,
onlyShowIcon: PropTypes.bool,
collapseOpened: PropTypes.bool,
verticalOpened: PropTypes.bool,
selected: PropTypes.number,
selectedIndex: PropTypes.number,
selectedItem: PropTypes.object, /* eslint-disable-line */
visibleItems: PropTypes.any, /* eslint-disable-line */
items: PropTypes.arrayOf(PropTypes.shape({
id: PropTypes.string,
label: PropTypes.string,
icon: PropTypes.string
})),
children: PropTypes.oneOfType([PropTypes.node, PropTypes.func, PropTypes.element]),
propForSelect: PropTypes.string,
opened: PropTypes.bool
};
export default AppNavComponent;