summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorFilipa Lacerda <filipa@gitlab.com>2017-08-14 12:18:02 +0000
committerJose Ivan Vargas <jvargas@gitlab.com>2017-09-01 13:14:15 -0500
commitd86631bca3f02c63d8a7203981ebc735ab0f993c (patch)
tree0619288c6c1c15a46f408bb3cd271063ae1e39ec /app
parent40392005760d3a093963859d57519740b691d54b (diff)
downloadgitlab-ce-d86631bca3f02c63d8a7203981ebc735ab0f993c.tar.gz
Merge branch 'fly-out-tunnel' into 'master'
Add dynamic navigation tunnel to fly-out menus Closes #35949 See merge request !13315
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/fly_out_nav.js159
-rw-r--r--app/assets/stylesheets/new_sidebar.scss47
2 files changed, 135 insertions, 71 deletions
diff --git a/app/assets/javascripts/fly_out_nav.js b/app/assets/javascripts/fly_out_nav.js
index adf397ca0fe..883ed63541d 100644
--- a/app/assets/javascripts/fly_out_nav.js
+++ b/app/assets/javascripts/fly_out_nav.js
@@ -1,21 +1,51 @@
+import Cookies from 'js-cookie';
import bp from './breakpoints';
-let headerHeight = 50;
-let sidebar;
+const HIDE_INTERVAL_TIMEOUT = 300;
+const IS_OVER_CLASS = 'is-over';
+const IS_ABOVE_CLASS = 'is-above';
+const IS_SHOWING_FLY_OUT_CLASS = 'is-showing-fly-out';
+let currentOpenMenu = null;
+let menuCornerLocs;
+let timeoutId;
-export const setSidebar = (el) => { sidebar = el; };
+export const mousePos = [];
-export const getHeaderHeight = () => headerHeight;
+export const setOpenMenu = (menu = null) => { currentOpenMenu = menu; };
+
+export const slope = (a, b) => (b.y - a.y) / (b.x - a.x);
export const canShowActiveSubItems = (el) => {
- if (el.classList.contains('active') && (sidebar && !sidebar.classList.contains('sidebar-icons-only'))) {
- return false;
+ const isHiddenByMedia = bp.getBreakpointSize() === 'sm' || bp.getBreakpointSize() === 'md';
+
+ if (el.classList.contains('active') && !isHiddenByMedia) {
+ return Cookies.get('sidebar_collapsed') === 'true';
}
return true;
};
+
export const canShowSubItems = () => bp.getBreakpointSize() === 'sm' || bp.getBreakpointSize() === 'md' || bp.getBreakpointSize() === 'lg';
+export const getHideSubItemsInterval = () => {
+ if (!currentOpenMenu) return 0;
+
+ const currentMousePos = mousePos[mousePos.length - 1];
+ const prevMousePos = mousePos[0];
+ const currentMousePosY = currentMousePos.y;
+ const [menuTop, menuBottom] = menuCornerLocs;
+
+ if (currentMousePosY < menuTop.y ||
+ currentMousePosY > menuBottom.y) return 0;
+
+ if (slope(prevMousePos, menuBottom) < slope(currentMousePos, menuBottom) &&
+ slope(prevMousePos, menuTop) > slope(currentMousePos, menuTop)) {
+ return HIDE_INTERVAL_TIMEOUT;
+ }
+
+ return 0;
+};
+
export const calculateTop = (boundingRect, outerHeight) => {
const windowHeight = window.innerHeight;
const bottomOverflow = windowHeight - (boundingRect.top + outerHeight);
@@ -24,51 +54,118 @@ export const calculateTop = (boundingRect, outerHeight) => {
boundingRect.top;
};
-export const showSubLevelItems = (el) => {
- const subItems = el.querySelector('.sidebar-sub-level-items');
+export const hideMenu = (el) => {
+ if (!el) return;
- if (!subItems || !canShowSubItems() || !canShowActiveSubItems(el)) return;
+ const parentEl = el.parentNode;
- subItems.style.display = 'block';
- el.classList.add('is-showing-fly-out');
- el.classList.add('is-over');
+ el.style.display = ''; // eslint-disable-line no-param-reassign
+ el.style.transform = ''; // eslint-disable-line no-param-reassign
+ el.classList.remove(IS_ABOVE_CLASS);
+ parentEl.classList.remove(IS_OVER_CLASS);
+ parentEl.classList.remove(IS_SHOWING_FLY_OUT_CLASS);
+
+ setOpenMenu();
+};
+export const moveSubItemsToPosition = (el, subItems) => {
const boundingRect = el.getBoundingClientRect();
const top = calculateTop(boundingRect, subItems.offsetHeight);
const isAbove = top < boundingRect.top;
subItems.classList.add('fly-out-list');
- subItems.style.transform = `translate3d(0, ${Math.floor(top) - headerHeight}px, 0)`;
+ subItems.style.transform = `translate3d(0, ${Math.floor(top)}px, 0)`; // eslint-disable-line no-param-reassign
+
+ const subItemsRect = subItems.getBoundingClientRect();
+
+ menuCornerLocs = [
+ {
+ x: subItemsRect.left, // left position of the sub items
+ y: subItemsRect.top, // top position of the sub items
+ },
+ {
+ x: subItemsRect.left, // left position of the sub items
+ y: subItemsRect.top + subItemsRect.height, // bottom position of the sub items
+ },
+ ];
if (isAbove) {
- subItems.classList.add('is-above');
+ subItems.classList.add(IS_ABOVE_CLASS);
}
};
-export const hideSubLevelItems = (el) => {
+export const showSubLevelItems = (el) => {
+ const subItems = el.querySelector('.sidebar-sub-level-items');
+
+ if (!canShowSubItems() || !canShowActiveSubItems(el)) return;
+
+ el.classList.add(IS_OVER_CLASS);
+
+ if (!subItems) return;
+
+ subItems.style.display = 'block';
+ el.classList.add(IS_SHOWING_FLY_OUT_CLASS);
+
+ setOpenMenu(subItems);
+ moveSubItemsToPosition(el, subItems);
+};
+
+export const mouseEnterTopItems = (el) => {
+ clearTimeout(timeoutId);
+
+ timeoutId = setTimeout(() => {
+ if (currentOpenMenu) hideMenu(currentOpenMenu);
+
+ showSubLevelItems(el);
+ }, getHideSubItemsInterval());
+};
+
+export const mouseLeaveTopItem = (el) => {
const subItems = el.querySelector('.sidebar-sub-level-items');
- if (!subItems || !canShowSubItems() || !canShowActiveSubItems(el)) return;
+ if (!canShowSubItems() || !canShowActiveSubItems(el) ||
+ (subItems && subItems === currentOpenMenu)) return;
- el.classList.remove('is-showing-fly-out');
- el.classList.remove('is-over');
- subItems.style.display = '';
- subItems.style.transform = '';
- subItems.classList.remove('is-above');
+ el.classList.remove(IS_OVER_CLASS);
+};
+
+export const documentMouseMove = (e) => {
+ mousePos.push({
+ x: e.clientX,
+ y: e.clientY,
+ });
+
+ if (mousePos.length > 6) mousePos.shift();
};
export default () => {
- const items = [...document.querySelectorAll('.sidebar-top-level-items > li')]
- .filter(el => el.querySelector('.sidebar-sub-level-items'));
+ const sidebar = document.querySelector('.sidebar-top-level-items');
- sidebar = document.querySelector('.nav-sidebar');
+ if (!sidebar) return;
- if (sidebar) {
- headerHeight = sidebar.offsetTop;
+ const items = [...sidebar.querySelectorAll('.sidebar-top-level-items > li')];
- items.forEach((el) => {
- el.addEventListener('mouseenter', e => showSubLevelItems(e.currentTarget));
- el.addEventListener('mouseleave', e => hideSubLevelItems(e.currentTarget));
- });
- }
+ sidebar.addEventListener('mouseleave', () => {
+ clearTimeout(timeoutId);
+
+ timeoutId = setTimeout(() => {
+ if (currentOpenMenu) hideMenu(currentOpenMenu);
+ }, getHideSubItemsInterval());
+ });
+
+ items.forEach((el) => {
+ const subItems = el.querySelector('.sidebar-sub-level-items');
+
+ if (subItems) {
+ subItems.addEventListener('mouseleave', () => {
+ clearTimeout(timeoutId);
+ hideMenu(currentOpenMenu);
+ });
+ }
+
+ el.addEventListener('mouseenter', e => mouseEnterTopItems(e.currentTarget));
+ el.addEventListener('mouseleave', e => mouseLeaveTopItem(e.currentTarget));
+ });
+
+ document.addEventListener('mousemove', documentMouseMove);
};
diff --git a/app/assets/stylesheets/new_sidebar.scss b/app/assets/stylesheets/new_sidebar.scss
index 0d13c742502..faedd207e01 100644
--- a/app/assets/stylesheets/new_sidebar.scss
+++ b/app/assets/stylesheets/new_sidebar.scss
@@ -70,7 +70,8 @@ $new-sidebar-collapsed-width: 50px;
background-color: $white-light;
}
- .sidebar-context-title {
+ .project-title,
+ .group-title {
overflow: hidden;
text-overflow: ellipsis;
}
@@ -96,29 +97,21 @@ $new-sidebar-collapsed-width: 50px;
top: $header-height;
bottom: 0;
left: 0;
+ overflow: auto;
background-color: $gray-normal;
box-shadow: inset -2px 0 0 $border-color;
- transform: translate3d(0, 0, 0);
&.sidebar-icons-only {
width: $new-sidebar-collapsed-width;
overflow-x: hidden;
- .nav-sidebar-inner-scroll {
- overflow-x: hidden;
- }
-
.badge,
- .sidebar-context-title {
+ .project-title {
display: none;
}
.nav-item-name {
- display: none;
- }
-
- .sidebar-top-level-items > li > a {
- min-height: 44px;
+ opacity: 0;
}
}
@@ -183,12 +176,6 @@ $new-sidebar-collapsed-width: 50px;
}
}
-.nav-sidebar-inner-scroll {
- height: 100%;
- width: 100%;
- overflow: auto;
-}
-
.with-performance-bar .nav-sidebar {
top: $header-height + $performance-bar-height;
}
@@ -263,32 +250,13 @@ $new-sidebar-collapsed-width: 50px;
position: absolute;
top: -30px;
bottom: -30px;
- left: 0;
+ left: -10px;
right: -30px;
z-index: -1;
}
- &::after {
- content: "";
- position: absolute;
- top: 44px;
- left: -30px;
- right: 35px;
- bottom: 0;
- height: 100%;
- max-height: 150px;
- z-index: -1;
- transform: skew(33deg);
- }
-
&.is-above {
margin-top: 1px;
-
- &::after {
- top: auto;
- bottom: 44px;
- transform: skew(-30deg);
- }
}
> .active {
@@ -335,8 +303,7 @@ $new-sidebar-collapsed-width: 50px;
}
}
- &:not(.active):hover > a,
- > a:hover,
+ &.active > a:hover,
&.is-over > a {
background-color: $white-light;
}