From 3ee83a645b9e4da8f4c0f2e6cbf772f504d8e9a9 Mon Sep 17 00:00:00 2001 From: Robert Messerle Date: Thu, 4 Dec 2014 13:30:42 -0800 Subject: [PATCH] feat(tabs): adds default transitions for tab content Closes #1044. By default, now tab content will animate in from whichever direction the ink-bar is moving. These animations can be overridden through the following classes: When a tab is selected, the following classes are added in order: 1. `md-transition-rtl`: flags the animation as right-to-left 2. `ng-animate`: added at the start of all animations 3. `ng-hide-remove` 4. `ng-hide-remove-active` When a tab is deselected, the following classes are added in order: 1. `md-transition-rtl`: flags the animation as right-to-left 2. `ng-animate`: added at the start of all animations 3. `ng-hide-add` 4. `ng-hide-add-active` Closes #717. Closes #811. refactor(tabs): removes unnecessary check --- src/components/tabs/js/tabItemController.js | 10 ++++- src/components/tabs/js/tabsController.js | 45 +++++++++++---------- src/components/tabs/js/tabsDirective.js | 6 ++- src/components/tabs/tabs.scss | 34 ++++++++++++++-- 4 files changed, 67 insertions(+), 28 deletions(-) diff --git a/src/components/tabs/js/tabItemController.js b/src/components/tabs/js/tabItemController.js index bb5f61effe4..b0889fd421b 100644 --- a/src/components/tabs/js/tabItemController.js +++ b/src/components/tabs/js/tabItemController.js @@ -52,7 +52,11 @@ function TabItemController($scope, $element, $attrs, $compile, $animate, $mdUtil }); } - function onSelect() { + function toggleAnimationClass(rightToLeft) { + self.contentContainer[rightToLeft ? 'addClass' : 'removeClass']('md-transition-rtl'); + } + + function onSelect(rightToLeft) { // Resume watchers and events firing when tab is selected $mdUtil.reconnectScope(self.contentScope); self.hammertime.on('swipeleft swiperight', $scope.onSwipe); @@ -60,12 +64,13 @@ function TabItemController($scope, $element, $attrs, $compile, $animate, $mdUtil $element.addClass('active'); $element.attr('aria-selected', true); $element.attr('tabIndex', 0); + toggleAnimationClass(rightToLeft); $animate.removeClass(self.contentContainer, 'ng-hide'); $scope.onSelect(); } - function onDeselect() { + function onDeselect(rightToLeft) { // Stop watchers & events from firing while tab is deselected $mdUtil.disconnectScope(self.contentScope); self.hammertime.off('swipeleft swiperight', $scope.onSwipe); @@ -74,6 +79,7 @@ function TabItemController($scope, $element, $attrs, $compile, $animate, $mdUtil $element.attr('aria-selected', false); // Only allow tabbing to the active tab $element.attr('tabIndex', -1); + toggleAnimationClass(rightToLeft); $animate.addClass(self.contentContainer, 'ng-hide'); $scope.onDeselect(); diff --git a/src/components/tabs/js/tabsController.js b/src/components/tabs/js/tabsController.js index bd22dfc0086..06d13e69210 100644 --- a/src/components/tabs/js/tabsController.js +++ b/src/components/tabs/js/tabsController.js @@ -13,14 +13,14 @@ function MdTabsController($scope, $element, $mdUtil, $$rAF) { self.$element = $element; self.scope = $scope; // The section containing the tab content $elements - self.contentArea = angular.element($element[0].querySelector('.md-tabs-content')); + var contentArea = self.contentArea = angular.element($element[0].querySelector('.md-tabs-content')); // Methods from iterator - self.inRange = tabsList.inRange; - self.indexOf = tabsList.indexOf; - self.itemAt = tabsList.itemAt; + var inRange = self.inRange = tabsList.inRange; + var indexOf = self.indexOf = tabsList.indexOf; + var itemAt = self.itemAt = tabsList.itemAt; self.count = tabsList.count; - + self.getSelectedItem = getSelectedItem; self.getSelectedIndex = getSelectedIndex; self.add = add; @@ -34,15 +34,15 @@ function MdTabsController($scope, $element, $mdUtil, $$rAF) { self.previous = previous; $scope.$on('$destroy', function() { - self.deselect(self.getSelectedItem()); + deselect(getSelectedItem()); for (var i = tabsList.count() - 1; i >= 0; i--) { - self.remove(tabsList[i], true); + remove(tabsList[i], true); } }); // Get the selected tab function getSelectedItem() { - return self.itemAt($scope.selectedIndex); + return itemAt($scope.selectedIndex); } function getSelectedIndex() { @@ -72,11 +72,11 @@ function MdTabsController($scope, $element, $mdUtil, $$rAF) { if (noReselect) { // do nothing - } else if (self.getSelectedItem() === tab) { + } else if (getSelectedItem() === tab) { if (tabsList.count() > 1) { - self.select(self.previous() || self.next()); + select(previous() || next()); } else { - self.deselect(tab); + deselect(tab); } } @@ -88,24 +88,27 @@ function MdTabsController($scope, $element, $mdUtil, $$rAF) { // Move a tab (used when ng-repeat order changes) function move(tab, toIndex) { - var isSelected = self.getSelectedItem() === tab; + var isSelected = getSelectedItem() === tab; tabsList.remove(tab); tabsList.add(tab, toIndex); - if (isSelected) self.select(tab); + if (isSelected) select(tab); $scope.$broadcast('$mdTabsChanged'); } - function select(tab) { + function select(tab, rightToLeft) { if (!tab || tab.isSelected || tab.isDisabled()) return; if (!tabsList.contains(tab)) return; - self.deselect(self.getSelectedItem()); + if (!angular.isDefined(rightToLeft)) { + rightToLeft = indexOf(tab) < $scope.selectedIndex; + } + deselect(getSelectedItem(), rightToLeft); - $scope.selectedIndex = self.indexOf(tab); + $scope.selectedIndex = indexOf(tab); tab.isSelected = true; - tab.onSelect(); + tab.onSelect(rightToLeft); $scope.$broadcast('$mdTabsChanged'); } @@ -115,20 +118,20 @@ function MdTabsController($scope, $element, $mdUtil, $$rAF) { self.tabToFocus = tab; } - function deselect(tab) { + function deselect(tab, rightToLeft) { if (!tab || !tab.isSelected) return; if (!tabsList.contains(tab)) return; $scope.selectedIndex = -1; tab.isSelected = false; - tab.onDeselect(); + tab.onDeselect(rightToLeft); } function next(tab, filterFn) { - return tabsList.next(tab || self.getSelectedItem(), filterFn || isTabEnabled); + return tabsList.next(tab || getSelectedItem(), filterFn || isTabEnabled); } function previous(tab, filterFn) { - return tabsList.previous(tab || self.getSelectedItem(), filterFn || isTabEnabled); + return tabsList.previous(tab || getSelectedItem(), filterFn || isTabEnabled); } function isTabEnabled(tab) { diff --git a/src/components/tabs/js/tabsDirective.js b/src/components/tabs/js/tabsDirective.js index 24231b738d5..2ff12dd090c 100644 --- a/src/components/tabs/js/tabsDirective.js +++ b/src/components/tabs/js/tabsDirective.js @@ -149,7 +149,9 @@ function TabsDirective($mdTheming) { function watchSelected() { scope.$watch('selectedIndex', function watchSelectedIndex(newIndex, oldIndex) { - tabsCtrl.deselect(tabsCtrl.itemAt(oldIndex)); + if (oldIndex == newIndex) return; + var rightToLeft = oldIndex > newIndex; + tabsCtrl.deselect(tabsCtrl.itemAt(oldIndex), rightToLeft); if (tabsCtrl.inRange(newIndex)) { var newTab = tabsCtrl.itemAt(newIndex); @@ -158,7 +160,7 @@ function TabsDirective($mdTheming) { ? tabsCtrl.next(newTab) : tabsCtrl.previous(newTab); } - tabsCtrl.select(newTab); + tabsCtrl.select(newTab, rightToLeft); } }); } diff --git a/src/components/tabs/tabs.scss b/src/components/tabs/tabs.scss index a9acc5f15fb..78d285fcff1 100644 --- a/src/components/tabs/tabs.scss +++ b/src/components/tabs/tabs.scss @@ -81,9 +81,38 @@ md-tabs[center] .md-header:not(.md-paginating) .md-header-items { .md-tabs-content { overflow: hidden; width: 100%; - + position: relative; .md-tab-content { - height : 100%; + height: 100%; + &.ng-hide { + &.ng-animate { + display: block !important; + } + } + &.ng-animate { + transition: transform $swift-ease-in-out-duration $swift-ease-in-out-timing-function; + transform: translateX(0); + &.ng-hide-add { + transform: translateX(-100%); + &.md-transition-rtl { + transform: translateX(100%); + } + } + &.ng-hide-remove { + position: absolute; + transform: translateX(100%); + top: 0; + left: 0; + right: 0; + bottom: 0; + &.md-transition-rtl { + transform: translateX(-100%); + } + &.ng-hide-remove-active { + transform: translateX(0); + } + } + } } } @@ -143,5 +172,4 @@ md-tab { opacity: 1; overflow: hidden; } - }