Skip to content

Commit b89b094

Browse files
committed
feat(sidenav): track sidenav vissibilty and enter / leave backdrop if needed.
- Revert scope value if sidenav is shown. - Throttle Resize Events Fixes angular#4595
1 parent 8ef798f commit b89b094

File tree

1 file changed

+61
-8
lines changed

1 file changed

+61
-8
lines changed

src/components/sidenav/sidenav.js

+61-8
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ function SidenavFocusDirective() {
208208
* - `<md-sidenav md-is-locked-open="$mdMedia('min-width: 1000px')"></md-sidenav>`
209209
* - `<md-sidenav md-is-locked-open="$mdMedia('sm')"></md-sidenav>` (locks open on small screens)
210210
*/
211-
function SidenavDirective($mdMedia, $mdUtil, $mdConstant, $mdTheming, $animate, $compile, $parse, $log, $q, $document) {
211+
function SidenavDirective($mdMedia, $mdUtil, $mdConstant, $mdTheming, $animate, $compile, $parse, $log, $q, $document, $$rAF) {
212212
return {
213213
restrict: 'E',
214214
scope: {
@@ -229,6 +229,9 @@ function SidenavDirective($mdMedia, $mdUtil, $mdConstant, $mdTheming, $animate,
229229
var lastParentOverFlow;
230230
var triggeringElement = null;
231231
var promise = $q.when(true);
232+
var skipSidenav = false;
233+
var skipNextUpdate = false;
234+
var $window = angular.element(window);
232235

233236
var isLockedOpenParsed = $parse(attr.mdIsLockedOpen);
234237
var isLocked = function() {
@@ -244,22 +247,55 @@ function SidenavDirective($mdMedia, $mdUtil, $mdConstant, $mdTheming, $animate,
244247

245248
$mdTheming.inherit(backdrop, element);
246249

250+
var throttleResize = $$rAF.throttle(revalidateVisibility);
251+
$window.on('resize', throttleResize);
252+
revalidateVisibility();
253+
247254
element.on('$destroy', function() {
248255
backdrop.remove();
249256
sidenavCtrl.destroy();
257+
$window.off('resize', throttleResize);
250258
});
251259

252260
scope.$on('$destroy', function(){
261+
$window.off('resize', throttleResize);
253262
backdrop.remove()
254263
});
255264

256265
scope.$watch(isLocked, updateIsLocked);
257266
scope.$watch('isOpen', updateIsOpen);
258267

259268

269+
260270
// Publish special accessor for the Controller instance
261271
sidenavCtrl.$toggleOpen = toggleOpen;
262272

273+
function revalidateVisibility() {
274+
var lastValue = scope.isOpen;
275+
if (attr.mdIsLockedOpen) return;
276+
277+
if (isHidden(element, true) && scope.isOpen) {
278+
scope.isOpen = false;
279+
} else if (!isHidden(element, true) && !scope.isOpen) {
280+
scope.isOpen = true;
281+
}
282+
283+
// If the revalidated isOpen Value got changed,
284+
// we should apply it to the view without running the updateOpen watcher
285+
if (lastValue != scope.isOpen) {
286+
skipSidenav = true;
287+
if (!scope.$$phase) scope.$apply();
288+
skipSidenav = false;
289+
}
290+
}
291+
292+
// Check if the element is hidden by any hide attribute (using computed style)
293+
function isHidden(element, compute) {
294+
if (element[0].offsetParent == null) return true;
295+
if (!compute) return false;
296+
return window.getComputedStyle(element[0]).display === 'none';
297+
}
298+
263299
/**
264300
* Toggle the DOM classes to indicate `locked`
265301
* @param isLocked
@@ -278,26 +314,43 @@ function SidenavDirective($mdMedia, $mdUtil, $mdConstant, $mdTheming, $animate,
278314
* Toggle the SideNav view and attach/detach listeners
279315
* @param isOpen
280316
*/
281-
function updateIsOpen(isOpen) {
317+
function updateIsOpen(isOpen, oldValue) {
318+
if (skipNextUpdate) {
319+
skipNextUpdate = false;
320+
return;
321+
}
322+
282323
// Support deprecated md-sidenav-focus attribute as fallback
283324
var focusEl = $mdUtil.findFocusTarget(element) || $mdUtil.findFocusTarget(element,'[md-sidenav-focus]') || element;
284325
var parent = element.parent();
285326

327+
// little hack to check show abbility
328+
if (!skipSidenav) {
329+
var wasClosed = element.hasClass('md-closed');
330+
element.removeClass('md-closed');
331+
if (isHidden(element, false) && !skipSidenav) {
332+
element.toggleClass('md-closed', wasClosed);
333+
skipNextUpdate = true;
334+
scope.isOpen = oldValue;
335+
return;
336+
}
337+
element.toggleClass('md-closed', wasClosed);
338+
}
339+
286340
parent[isOpen ? 'on' : 'off']('keydown', onKeyDown);
287341
backdrop[isOpen ? 'on' : 'off']('click', close);
288342

289-
if ( isOpen ) {
343+
if ( isOpen && !skipSidenav) {
290344
// Capture upon opening..
291345
triggeringElement = $document[0].activeElement;
292346
}
293347

294348
disableParentScroll(isOpen);
295349

296-
return promise = $q.all([
297-
isOpen ? $animate.enter(backdrop, parent) : $animate.leave(backdrop),
298-
$animate[isOpen ? 'removeClass' : 'addClass'](element, 'md-closed')
299-
])
300-
.then(function() {
350+
var actions = [isOpen ? $animate.enter(backdrop, parent) : $animate.leave(backdrop)];
351+
if (!skipSidenav) actions.push(isOpen ? $animate.removeClass(element, 'md-closed') : $animate.addClass(element, 'md-closed'));
352+
353+
return promise = $q.all(actions).then(function() {
301354
// Perform focus when animations are ALL done...
302355
if (scope.isOpen) {
303356
focusEl && focusEl.focus();

0 commit comments

Comments
 (0)