mirror of
https://github.com/fleetdm/fleet
synced 2026-05-23 08:58:41 +00:00
Responsive nav (#225)
* Allow specifying debounce options * responsive nav styles * animate sub items in skinny nav
This commit is contained in:
parent
26b1e70ac3
commit
c4924be4d4
4 changed files with 142 additions and 41 deletions
|
|
@ -1,7 +1,7 @@
|
|||
import React, { Component, PropTypes } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { noop } from 'lodash';
|
||||
import { Style } from 'radium';
|
||||
import { Style, StyleRoot } from 'radium';
|
||||
import { fetchCurrentUser } from '../../redux/nodes/auth/actions';
|
||||
import Footer from './Footer';
|
||||
import globalStyles from '../../styles/global';
|
||||
|
|
@ -32,11 +32,11 @@ export class App extends Component {
|
|||
const { children } = this.props;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<StyleRoot>
|
||||
<Style rules={globalStyles} />
|
||||
{children}
|
||||
<Footer />
|
||||
</div>
|
||||
</StyleRoot>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import React, { Component, PropTypes } from 'react';
|
||||
import radium from 'radium';
|
||||
import { isEqual, last } from 'lodash';
|
||||
import componentStyles from './styles';
|
||||
import kolideLogo from '../../../assets/images/kolide-logo.svg';
|
||||
|
|
@ -15,15 +16,7 @@ class SidePanel extends Component {
|
|||
this.state = {
|
||||
activeTab: 'Hosts',
|
||||
activeSubItem: 'Add Hosts',
|
||||
};
|
||||
}
|
||||
|
||||
setActiveTab = (activeTab) => {
|
||||
return (evt) => {
|
||||
evt.preventDefault();
|
||||
|
||||
this.setState({ activeTab });
|
||||
return false;
|
||||
showSubItems: false,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -36,6 +29,28 @@ class SidePanel extends Component {
|
|||
};
|
||||
}
|
||||
|
||||
setActiveTab = (activeTab) => {
|
||||
return (evt) => {
|
||||
evt.preventDefault();
|
||||
|
||||
this.setState({
|
||||
activeTab,
|
||||
});
|
||||
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
toggleShowSubItems = (showSubItems) => {
|
||||
return (evt) => {
|
||||
evt.preventDefault();
|
||||
|
||||
this.setState({ showSubItems });
|
||||
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
renderHeader = () => {
|
||||
const {
|
||||
user: {
|
||||
|
|
@ -141,15 +156,34 @@ class SidePanel extends Component {
|
|||
}
|
||||
|
||||
renderSubItems = (subItems) => {
|
||||
const { subItemsStyles } = componentStyles;
|
||||
const { renderSubItem } = this;
|
||||
const { subItemListStyles, subItemsStyles } = componentStyles;
|
||||
const { renderCollapseSubItems, renderSubItem } = this;
|
||||
const { showSubItems } = this.state;
|
||||
|
||||
if (!subItems.length) return false;
|
||||
|
||||
return (
|
||||
<ul style={subItemsStyles}>
|
||||
{subItems.map(subItem => {
|
||||
return renderSubItem(subItem);
|
||||
})}
|
||||
</ul>
|
||||
<div style={subItemsStyles(showSubItems)}>
|
||||
<ul style={subItemListStyles(showSubItems)}>
|
||||
{subItems.map(subItem => {
|
||||
return renderSubItem(subItem);
|
||||
})}
|
||||
</ul>
|
||||
{renderCollapseSubItems()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderCollapseSubItems = () => {
|
||||
const { toggleShowSubItems } = this;
|
||||
const { showSubItems } = this.state;
|
||||
const { collapseSubItemsWrapper } = componentStyles;
|
||||
const iconName = showSubItems ? 'kolidecon-chevron-bold-left' : 'kolidecon-chevron-bold-right';
|
||||
|
||||
return (
|
||||
<div style={collapseSubItemsWrapper}>
|
||||
<i className={iconName} style={{ color: '#FFF' }} onClick={toggleShowSubItems(!showSubItems)} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -166,4 +200,4 @@ class SidePanel extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
export default SidePanel;
|
||||
export default radium(SidePanel);
|
||||
|
|
|
|||
|
|
@ -2,12 +2,15 @@ import Styles from '../../styles';
|
|||
|
||||
const { border, color, font, padding } = Styles;
|
||||
|
||||
export default {
|
||||
const componentStyles = {
|
||||
companyLogoStyles: {
|
||||
position: 'absolute',
|
||||
left: '16px',
|
||||
height: '44px',
|
||||
marginRight: '10px',
|
||||
'@media (max-width: 760px)': {
|
||||
left: '4px',
|
||||
},
|
||||
},
|
||||
headerStyles: {
|
||||
borderBottomColor: color.accentLight,
|
||||
|
|
@ -23,6 +26,10 @@ export default {
|
|||
fontSize: '22px',
|
||||
marginRight: '16px',
|
||||
top: '4px',
|
||||
left: 0,
|
||||
'@media (max-width: 760px)': {
|
||||
left: '5px',
|
||||
},
|
||||
},
|
||||
navItemBeforeStyles: {
|
||||
content: '',
|
||||
|
|
@ -33,6 +40,9 @@ export default {
|
|||
top: '2px',
|
||||
bottom: 0,
|
||||
backgroundColor: '#9a61c6',
|
||||
'@media (max-width: 760px)': {
|
||||
left: 0,
|
||||
},
|
||||
},
|
||||
navItemListStyles: {
|
||||
listStyle: 'none',
|
||||
|
|
@ -42,10 +52,19 @@ export default {
|
|||
navItemNameStyles: {
|
||||
display: 'inline-block',
|
||||
textDecoration: 'none',
|
||||
'@media (max-width: 760px)': {
|
||||
display: 'none',
|
||||
},
|
||||
},
|
||||
navItemStyles: (active) => {
|
||||
const activeStyles = {
|
||||
color: color.brand,
|
||||
borderBottom: 'none',
|
||||
transition: 'none',
|
||||
'@media (max-width: 760px)': {
|
||||
borderBottom: '8px solid #9a61c6',
|
||||
textAlign: 'center',
|
||||
},
|
||||
};
|
||||
|
||||
const baseStyles = {
|
||||
|
|
@ -56,9 +75,10 @@ export default {
|
|||
fontSize: font.small,
|
||||
textTransform: 'uppercase',
|
||||
paddingTop: padding.half,
|
||||
WebkitTransition: 'all 0.2s ease-in-out',
|
||||
MozTransition: 'all 0.2s ease-in-out',
|
||||
transition: 'all 0.2s ease-in-out',
|
||||
'@media (max-width: 760px)': {
|
||||
textAlign: 'center',
|
||||
},
|
||||
};
|
||||
|
||||
if (active) {
|
||||
|
|
@ -80,6 +100,9 @@ export default {
|
|||
borderTopWidth: '1px',
|
||||
marginRight: '16px',
|
||||
marginTop: '5px',
|
||||
'@media (max-width: 760px)': {
|
||||
marginRight: 0,
|
||||
},
|
||||
};
|
||||
|
||||
if (lastChild) {
|
||||
|
|
@ -104,6 +127,10 @@ export default {
|
|||
position: 'fixed',
|
||||
top: 0,
|
||||
width: '216px',
|
||||
'@media (max-width: 760px)': {
|
||||
paddingLeft: 0,
|
||||
width: '54px',
|
||||
},
|
||||
},
|
||||
orgNameStyles: {
|
||||
fontSize: font.medium,
|
||||
|
|
@ -115,6 +142,9 @@ export default {
|
|||
textOverflow: 'ellipsis',
|
||||
top: '3px',
|
||||
whiteSpace: 'nowrap',
|
||||
'@media (max-width: 760px)': {
|
||||
display: 'none',
|
||||
},
|
||||
},
|
||||
subItemBeforeStyles: {
|
||||
backgroundColor: color.white,
|
||||
|
|
@ -152,8 +182,6 @@ export default {
|
|||
paddingLeft: padding.most,
|
||||
paddingTop: padding.xSmall,
|
||||
position: 'relative',
|
||||
WebkitTransition: 'all 0.2s ease-in-out',
|
||||
MozTransition: 'all 0.2s ease-in-out',
|
||||
textTransform: 'none',
|
||||
transition: 'all 0.2s ease-in-out',
|
||||
};
|
||||
|
|
@ -167,17 +195,46 @@ export default {
|
|||
|
||||
return baseStyles;
|
||||
},
|
||||
subItemsStyles: {
|
||||
backgroundColor: color.brand,
|
||||
boxShadow: 'inset 0 5px 8px 0 rgba(0, 0, 0, 0.12), inset 0 -5px 8px 0 rgba(0, 0, 0, 0.12)',
|
||||
listStyle: 'none',
|
||||
marginBottom: 0,
|
||||
marginLeft: '-24px',
|
||||
marginRight: 0,
|
||||
marginTop: padding.medium,
|
||||
minHeight: '6px',
|
||||
paddingBottom: padding.half,
|
||||
paddingTop: padding.half,
|
||||
subItemsStyles: (expanded) => {
|
||||
return {
|
||||
backgroundColor: color.brand,
|
||||
boxShadow: 'inset 0 5px 8px 0 rgba(0, 0, 0, 0.12), inset 0 -5px 8px 0 rgba(0, 0, 0, 0.12)',
|
||||
marginBottom: 0,
|
||||
marginRight: 0,
|
||||
minHeight: '87px',
|
||||
paddingBottom: padding.half,
|
||||
paddingTop: padding.half,
|
||||
marginLeft: '-24px',
|
||||
marginTop: padding.medium,
|
||||
transition: 'width 0.1s ease-in-out',
|
||||
'@media (max-width: 760px)': {
|
||||
bottom: '-8px',
|
||||
left: '54px',
|
||||
marginLeft: 0,
|
||||
position: 'absolute',
|
||||
width: expanded ? '251px' : '18px',
|
||||
},
|
||||
};
|
||||
},
|
||||
subItemListStyles: (expanded) => {
|
||||
return {
|
||||
listStyle: 'none',
|
||||
'@media (max-width: 760px)': {
|
||||
borderRight: '1px solid rgba(0,0,0,0.16)',
|
||||
display: expanded ? 'inline-block' : 'none',
|
||||
padding: 0,
|
||||
textAlign: 'left',
|
||||
width: '211px',
|
||||
},
|
||||
};
|
||||
},
|
||||
collapseSubItemsWrapper: {
|
||||
position: 'absolute',
|
||||
right: '3px',
|
||||
top: '41%',
|
||||
'@media (min-width: 761px)': {
|
||||
display: 'none',
|
||||
},
|
||||
},
|
||||
usernameStyles: {
|
||||
position: 'relative',
|
||||
|
|
@ -187,6 +244,9 @@ export default {
|
|||
padding: 0,
|
||||
fontSize: font.small,
|
||||
textTransform: 'uppercase',
|
||||
'@media (max-width: 760px)': {
|
||||
display: 'none',
|
||||
},
|
||||
},
|
||||
userStatusStyles: (enabled) => {
|
||||
const backgroundColor = enabled ? color.success : color.warning;
|
||||
|
|
@ -202,6 +262,11 @@ export default {
|
|||
position: 'relative',
|
||||
top: '6px',
|
||||
width: size,
|
||||
'@media (max-width: 760px)': {
|
||||
display: 'none',
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
export default componentStyles;
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
import { debounce } from 'lodash';
|
||||
|
||||
const TIMEOUT = 1000; // only allow 1 click per second
|
||||
const DEFAULT_TIMEOUT = 1000; // 1 function execution per second by default
|
||||
|
||||
export default (func) => {
|
||||
return debounce(func, TIMEOUT, {
|
||||
leading: true,
|
||||
trailing: false,
|
||||
export default (func, options = {}) => {
|
||||
const { leading = true, trailing = false, timeout = DEFAULT_TIMEOUT } = options;
|
||||
|
||||
return debounce(func, timeout, {
|
||||
leading,
|
||||
trailing,
|
||||
});
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue