Skip to content
This repository has been archived by the owner on Sep 5, 2024. It is now read-only.

feat(sidenav): add lock-open attribute #446

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion docs/config/template/index.template.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@
</head>
<body layout="horizontal">

<md-sidenav layout="vertical" class="md-sidenav-left md-whiteframe-z3" component-id="left">
<md-sidenav layout="vertical"
class="md-sidenav-left md-whiteframe-z2"
component-id="left"
lock-open="$media('md')">

<md-toolbar class="md-theme-light md-medium-tall">
<h1 class="md-toolbar-tools" style="padding-top:25px;">
Expand Down
58 changes: 30 additions & 28 deletions src/components/sidenav/_sidenav.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,53 +3,55 @@ md-sidenav {

width: $sidenav-default-width;
bottom: 0;

z-index: $z-index-sidenav;
background-color: white;
overflow: auto;

transition: transform 0.3s ease-in-out;
display: none;
&.open,
&.open-add,
&.open-remove {
display: block;
}
&.open-add,
&.open-remove {
/* this is required as of 1.3x to properly
apply all styling in a show/hide animation */
transition: 0s all;
}
&.open-add.open-add-active,
&.open-remove.open-remove-active {
transition: transform 0.3s ease-in-out;
}

@extend .md-sidenav-left;
&.lock-open,
&.lock-open.material-sidenav-left,
&.lock-open.material-sidenav-right {
position: static;
display: block;
transform: translate3d(0, 0, 0);
}

// &.closed {
// display: none;
// }
@extend .material-sidenav-left;
}
.md-sidenav-backdrop.lock-open {
display: none;
}

.md-sidenav-left {
left: 0;
top: 0;
transform: translate3d(-100%, 0, 0);

&.open {
transform: translate3d(0%, 0, 0);
z-index: $z-index-sidenav;
}
}

.md-sidenav-right {
left: 100%;
top: 0;
transform: translate3d(100%, 0, 0);

transform: translate3d(0%, 0, 0);
&.open {
transform: translate3d(-100%, 0, 0);
z-index: $z-index-sidenav;
}
}

@media (min-width: $layout-breakpoint-md) {
md-sidenav {
position: static;
transform: translate3d(0,0,0) !important;
}

.md-sidenav-backdrop {
display: none !important;
}
}

@media (max-width: $sidenav-default-width + 2 * $sidenav-min-room) {
md-sidenav {
max-width: 75%;
}
}
20 changes: 9 additions & 11 deletions src/components/sidenav/demoBasicUsage/index.html
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@

<div ng-controller="AppCtrl" layout="vertical">
<div ng-controller="AppCtrl" layout="vertical" layout-fill>

<section layout="horizontal" flex>

<md-sidenav class="md-sidenav-left md-whiteframe-z2" component-id="left">
<md-sidenav class="md-sidenav-left md-whiteframe-z2" component-id="left" lock-open="$media('md')">

<md-toolbar class="md-theme-indigo">
<h1 class="md-toolbar-tools">Sidenav Left</h1>
Expand All @@ -12,6 +12,10 @@ <h1 class="md-toolbar-tools">Sidenav Left</h1>
<md-button ng-click="close()" class="md-button-colored" hide-md>
Close Sidenav Left
</md-button>
<p hide show-md>
This sidenav is locked open on your device. To go back to the default behavior,
narrow your display.
</p>
</md-content>

</md-sidenav>
Expand All @@ -20,10 +24,7 @@ <h1 class="md-toolbar-tools">Sidenav Left</h1>

<div layout="vertical" layout-fill layout-align="center center">
<p>
On smaller devices, md-sidenav will be hidden by default.
</p>
<p>
On larger, it will be shown by default.
The left sidenav will 'lock open' on a medium (>=960px wide) device.
</p>

<div>
Expand All @@ -35,7 +36,7 @@ <h1 class="md-toolbar-tools">Sidenav Left</h1>

<div>
<md-button ng-click="toggleRight()"
class="md-button-colored" hide-md>
class="md-button-colored">
Toggle right
</md-button>
</div>
Expand All @@ -49,7 +50,7 @@ <h1 class="md-toolbar-tools">Sidenav Left</h1>
<h1 class="md-toolbar-tools">Sidenav Right</h1>
</md-toolbar>
<md-content ng-controller="RightCtrl" class="md-content-padding">
<md-button ng-click="close()" class="md-button-colored" hide-md>
<md-button ng-click="close()" class="md-button-colored">
Close Sidenav Right
</md-button>
</md-content>
Expand All @@ -59,6 +60,3 @@ <h1 class="md-toolbar-tools">Sidenav Right</h1>
</section>

</div>
<style>
p { text-align: center; }
</style>
132 changes: 51 additions & 81 deletions src/components/sidenav/sidenav.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
angular.module('material.components.sidenav', [
'material.core',
'material.services.registry',
'material.services.media',
'material.animations'
])
.factory('$mdSidenav', [
Expand All @@ -16,8 +17,9 @@ angular.module('material.components.sidenav', [
])
.directive('mdSidenav', [
'$timeout',
'$mdEffects',
'$$rAF',
'$animate',
'$parse',
'$mdMedia',
'$mdConstant',
mdSidenavDirective
])
Expand Down Expand Up @@ -49,24 +51,12 @@ function mdSidenavController($scope, $element, $attrs, $timeout, $mdSidenav, $md
this.isOpen = function() {
return !!$scope.isOpen;
};

/**
* Toggle the side menu to open or close depending on its current state.
*/
this.toggle = function() {
$scope.isOpen = !$scope.isOpen;
};

/**
* Open the side menu
*/
this.open = function() {
$scope.isOpen = true;
};

/**
* Close the side menu
*/
this.close = function() {
$scope.isOpen = false;
};
Expand Down Expand Up @@ -104,32 +94,16 @@ function mdSidenavService($mdComponentRegistry) {

return {
isOpen: function() {
if (!instance) { return; }
return instance.isOpen();
return instance && instance.isOpen();
},
/**
* Toggle the given sidenav
* @param handle the specific sidenav to toggle
*/
toggle: function() {
if(!instance) { return; }
instance.toggle();
instance && instance.toggle();
},
/**
* Open the given sidenav
* @param handle the specific sidenav to open
*/
open: function(handle) {
if(!instance) { return; }
instance.open();
open: function() {
instance && instance.open();
},
/**
* Close the given sidenav
* @param handle the specific sidenav to close
*/
close: function(handle) {
if(!instance) { return; }
instance.close();
close: function() {
instance && instance.close();
}
};
};
Expand All @@ -145,8 +119,8 @@ function mdSidenavService($mdComponentRegistry) {
*
* A Sidenav component that can be opened and closed programatically.
*
* When used properly with a layout, it will seamleslly stay open on medium
* and larger screens, while being hidden by default on mobile devices.
* When opened, it will appear above the app's main content area,
* unless a `lock-open` attribute is provided (see below).
*
* @usage
* <hljs lang="html">
Expand Down Expand Up @@ -176,69 +150,68 @@ function mdSidenavService($mdComponentRegistry) {
* };
* });
* </hljs>
*
* @param {string=} component-id componentId to use with $mdSidenav
* service.
* @param {expression=} lock-open When this expression evalutes to true,
* the sidenav 'locks open': it falls into the content's flow instead
* of appearing above it.
*
* A $media() function is exposed to the expression, which
* can be given a media query or one of the `sm`, `md` or `lg` presets.
* Examples:
*
* - `<md-sidenav lock-open="shouldLockOpen"></md-sidenav>`
* - `<md-sidenav lock-open="$media('min-width: 1000px')"></md-sidenav>`
* - `<md-sidenav lock-open="$media('sm')"></md-sidenav>` <!-- locks open on small screens !-->
*/
function mdSidenavDirective($timeout, $mdEffects, $$rAF, $mdConstant) {
function mdSidenavDirective($timeout, $animate, $parse, $mdMedia, $mdConstant) {
return {
restrict: 'E',
scope: {},
controller: '$mdSidenavController',
compile: compile
link: postLink
};

function compile(element, attr) {
element.addClass('closed');

return postLink;
}
function postLink(scope, element, attr, sidenavCtrl) {
var backdrop = angular.element('<md-backdrop class="md-sidenav-backdrop">');

scope.$watch('isOpen', onShowHideSide);
element.on($mdEffects.TRANSITIONEND_EVENT, onTransitionEnd);
var lockOpenParsed = $parse(attr.lockOpen);
var backdrop = angular.element(
'<md-backdrop class="md-sidenav-backdrop">'
);

scope.$watch('isOpen', setOpen);
scope.$watch(function() {
return lockOpenParsed(scope.$parent, {
$media: $mdMedia
});
}, function(isLocked) {
element.toggleClass('lock-open', !!isLocked);
backdrop.toggleClass('lock-open', !!isLocked);
});

/**
* Toggle the SideNav view and attach/detach listeners
* @param isOpen
*/
function onShowHideSide(isOpen) {
function setOpen(isOpen) {
var parent = element.parent();

if (isOpen) {
element.removeClass('closed');
parent[isOpen ? 'on' : 'off']('keydown', onKeyDown);
$animate[isOpen ? 'addClass' : 'removeClass'](element, 'open');

parent.append(backdrop);
backdrop.on('click', close);
parent.on('keydown', onKeyDown);

} else {
backdrop.remove();
backdrop.off('click', close);
parent.off('keydown', onKeyDown);
}

// Wait until the next frame, so that if the `closed` class was just removed the
// element has a chance to 're-initialize' from being display: none.
$$rAF(function() {
element.toggleClass('open', !!scope.isOpen);
});
}

function onTransitionEnd(ev) {
if (ev.target === element[0] && !scope.isOpen) {
element.addClass('closed');
}
$animate[isOpen ? 'enter' : 'leave'](backdrop, parent);
backdrop[isOpen ? 'on' : 'off']('click', close);
}

/**
* Auto-close sideNav when the `escape` key is pressed.
* @param evt
*/
function onKeyDown(evt) {
if(evt.which === $mdConstant.KEY_CODE.ESCAPE){
function onKeyDown(ev) {
if (ev.which === $mdConstant.KEY_CODE.ESCAPE) {
close();

evt.preventDefault();
evt.stopPropagation();
ev.preventDefault();
ev.stopPropagation();
}
}

Expand All @@ -248,9 +221,6 @@ function mdSidenavDirective($timeout, $mdEffects, $$rAF, $mdConstant) {
* to close() and perform its own actions.
*/
function close() {

onShowHideSide( false );

$timeout(function(){
sidenavCtrl.close();
});
Expand Down
Loading