Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Léo-Paul Géneau
gitlab-ce
Commits
20bfc4f6
Commit
20bfc4f6
authored
Aug 01, 2017
by
Phil Hughes
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
added mouseleave timeout with JS
parent
f20a4849
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
206 additions
and
20 deletions
+206
-20
app/assets/javascripts/fly_out_nav.js
app/assets/javascripts/fly_out_nav.js
+72
-18
app/assets/stylesheets/new_sidebar.scss
app/assets/stylesheets/new_sidebar.scss
+2
-1
spec/javascripts/fly_out_nav_spec.js
spec/javascripts/fly_out_nav_spec.js
+132
-1
No files found.
app/assets/javascripts/fly_out_nav.js
View file @
20bfc4f6
let
hideTimeoutInterval
=
0
;
let
hideTimeout
;
let
subitems
;
export
const
getHideTimeoutInterval
=
()
=>
hideTimeoutInterval
;
export
const
hideAllSubItems
=
()
=>
{
subitems
.
forEach
((
el
)
=>
{
el
.
parentNode
.
classList
.
remove
(
'
is-over
'
);
el
.
style
.
display
=
'
none
'
;
// eslint-disable-line no-param-reassign
});
};
export
const
calculateTop
=
(
boundingRect
,
outerHeight
)
=>
{
const
windowHeight
=
window
.
innerHeight
;
const
bottomOverflow
=
windowHeight
-
(
boundingRect
.
top
+
outerHeight
);
...
...
@@ -6,23 +19,64 @@ export const calculateTop = (boundingRect, outerHeight) => {
boundingRect
.
top
;
};
export
const
showSubLevelItems
=
(
el
)
=>
{
const
$subitems
=
el
.
querySelector
(
'
.sidebar-sub-level-items
'
);
if
(
!
$subitems
)
return
;
hideAllSubItems
();
if
(
el
.
classList
.
contains
(
'
is-over
'
))
{
clearTimeout
(
hideTimeout
);
}
else
{
$subitems
.
style
.
display
=
'
block
'
;
el
.
classList
.
add
(
'
is-over
'
);
}
const
boundingRect
=
el
.
getBoundingClientRect
();
const
top
=
calculateTop
(
boundingRect
,
$subitems
.
offsetHeight
);
const
isAbove
=
top
<
boundingRect
.
top
;
$subitems
.
style
.
transform
=
`translate3d(0,
${
top
}
px, 0)`
;
if
(
isAbove
)
{
$subitems
.
classList
.
add
(
'
is-above
'
);
}
};
export
const
hideSubLevelItems
=
(
el
)
=>
{
const
$subitems
=
el
.
querySelector
(
'
.sidebar-sub-level-items
'
);
const
hideFn
=
()
=>
{
el
.
classList
.
remove
(
'
is-over
'
);
$subitems
.
style
.
display
=
'
none
'
;
$subitems
.
classList
.
remove
(
'
is-above
'
);
hideTimeoutInterval
=
0
;
};
if
(
$subitems
&&
hideTimeoutInterval
)
{
hideTimeout
=
setTimeout
(
hideFn
,
hideTimeoutInterval
);
}
else
if
(
$subitems
)
{
hideFn
();
}
};
export
const
setMouseOutTimeout
=
(
el
)
=>
{
if
(
el
.
closest
(
'
.sidebar-sub-level-items
'
))
{
hideTimeoutInterval
=
250
;
}
else
{
hideTimeoutInterval
=
0
;
}
};
export
default
()
=>
{
$
(
'
.sidebar-top-level-items > li:not(.active)
'
).
on
(
'
mouseover
'
,
(
e
)
=>
{
const
$this
=
e
.
currentTarget
;
const
$subitems
=
$
(
'
.sidebar-sub-level-items
'
,
$this
).
show
();
if
(
$subitems
.
length
)
{
const
boundingRect
=
$this
.
getBoundingClientRect
();
const
top
=
calculateTop
(
boundingRect
,
$subitems
.
outerHeight
());
const
isAbove
=
top
<
boundingRect
.
top
;
$subitems
.
css
({
transform
:
`translate3d(0,
${
top
}
px, 0)`
,
});
if
(
isAbove
)
{
$subitems
.
addClass
(
'
is-above
'
);
}
}
}).
on
(
'
mouseout
'
,
e
=>
$
(
'
.sidebar-sub-level-items
'
,
e
.
currentTarget
).
hide
().
removeClass
(
'
is-above
'
));
const
items
=
[...
document
.
querySelectorAll
(
'
.sidebar-top-level-items > li:not(.active)
'
)];
subitems
=
[...
document
.
querySelectorAll
(
'
.sidebar-top-level-items > li:not(.active) .sidebar-sub-level-items
'
)];
items
.
forEach
((
el
)
=>
{
el
.
addEventListener
(
'
mouseenter
'
,
e
=>
showSubLevelItems
(
e
.
currentTarget
));
el
.
addEventListener
(
'
mouseleave
'
,
e
=>
hideSubLevelItems
(
e
.
currentTarget
));
});
subitems
.
forEach
(
el
=>
el
.
addEventListener
(
'
mouseleave
'
,
e
=>
setMouseOutTimeout
(
e
.
target
)));
};
app/assets/stylesheets/new_sidebar.scss
View file @
20bfc4f6
...
...
@@ -292,7 +292,8 @@ $new-sidebar-width: 220px;
}
&
:not
(
.active
)
:hover
>
a
,
>
a
:hover
{
>
a
:hover
,
&
.is-over
>
a
{
background-color
:
$white-light
;
}
}
...
...
spec/javascripts/fly_out_nav_spec.js
View file @
20bfc4f6
import
{
calculateTop
}
from
'
~/fly_out_nav
'
;
import
{
calculateTop
,
setMouseOutTimeout
,
getHideTimeoutInterval
,
hideSubLevelItems
,
showSubLevelItems
,
}
from
'
~/fly_out_nav
'
;
describe
(
'
Fly out sidebar navigation
'
,
()
=>
{
let
el
;
beforeEach
(()
=>
{
el
=
document
.
createElement
(
'
div
'
);
document
.
body
.
appendChild
(
el
);
});
afterEach
(()
=>
{
el
.
remove
();
});
describe
(
'
calculateTop
'
,
()
=>
{
it
(
'
returns boundingRect top
'
,
()
=>
{
const
boundingRect
=
{
...
...
@@ -24,4 +40,119 @@ describe('Fly out sidebar navigation', () => {
).
toBe
(
window
.
innerHeight
-
50
);
});
});
describe
(
'
setMouseOutTimeout
'
,
()
=>
{
it
(
'
sets hideTimeoutInterval to 150 when inside sub items
'
,
()
=>
{
el
.
innerHTML
=
'
<div class="sidebar-sub-level-items"><div class="js-test"></div></div>
'
;
setMouseOutTimeout
(
el
.
querySelector
(
'
.js-test
'
));
expect
(
getHideTimeoutInterval
(),
).
toBe
(
150
);
});
it
(
'
resets hideTimeoutInterval when not inside sub items
'
,
()
=>
{
setMouseOutTimeout
(
el
);
expect
(
getHideTimeoutInterval
(),
).
toBe
(
0
);
});
});
describe
(
'
hideSubLevelItems
'
,
()
=>
{
beforeEach
(()
=>
{
el
.
innerHTML
=
'
<div class="sidebar-sub-level-items"></div>
'
;
});
it
(
'
hides subitems
'
,
()
=>
{
hideSubLevelItems
(
el
);
expect
(
el
.
querySelector
(
'
.sidebar-sub-level-items
'
).
style
.
display
,
).
toBe
(
'
none
'
);
});
it
(
'
removes is-over class
'
,
()
=>
{
spyOn
(
el
.
classList
,
'
remove
'
);
hideSubLevelItems
(
el
);
expect
(
el
.
classList
.
remove
,
).
toHaveBeenCalledWith
(
'
is-over
'
);
});
it
(
'
removes is-above class from sub-items
'
,
()
=>
{
const
subItems
=
el
.
querySelector
(
'
.sidebar-sub-level-items
'
);
spyOn
(
subItems
.
classList
,
'
remove
'
);
hideSubLevelItems
(
el
);
expect
(
subItems
.
classList
.
remove
,
).
toHaveBeenCalledWith
(
'
is-above
'
);
});
it
(
'
does nothing if el has no sub-items
'
,
()
=>
{
el
.
innerHTML
=
''
;
spyOn
(
el
.
classList
,
'
remove
'
);
hideSubLevelItems
(
el
);
expect
(
el
.
classList
.
remove
,
).
not
.
toHaveBeenCalledWith
();
});
});
describe
(
'
showSubLevelItems
'
,
()
=>
{
beforeEach
(()
=>
{
el
.
innerHTML
=
'
<div class="sidebar-sub-level-items"></div>
'
;
});
it
(
'
adds is-over class to el
'
,
()
=>
{
spyOn
(
el
.
classList
,
'
add
'
);
showSubLevelItems
(
el
);
expect
(
el
.
classList
.
add
,
).
toHaveBeenCalledWith
(
'
is-over
'
);
});
it
(
'
shows sub-items
'
,
()
=>
{
showSubLevelItems
(
el
);
expect
(
el
.
querySelector
(
'
.sidebar-sub-level-items
'
).
style
.
display
,
).
toBe
(
'
block
'
);
});
it
(
'
sets transform of sub-items
'
,
()
=>
{
showSubLevelItems
(
el
);
expect
(
el
.
querySelector
(
'
.sidebar-sub-level-items
'
).
style
.
transform
,
).
toBe
(
`translate3d(0px,
${
el
.
offsetTop
}
px, 0px)`
);
});
it
(
'
sets is-above when element is above
'
,
()
=>
{
const
subItems
=
el
.
querySelector
(
'
.sidebar-sub-level-items
'
);
subItems
.
style
.
height
=
'
5000px
'
;
el
.
style
.
position
=
'
relative
'
;
el
.
style
.
top
=
'
1000px
'
;
spyOn
(
el
.
classList
,
'
add
'
);
showSubLevelItems
(
el
);
expect
(
el
.
classList
.
add
,
).
toHaveBeenCalledWith
(
'
is-above
'
);
});
});
});
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment