6
6
* @ngInject
7
7
*/
8
8
function MdTabsController ( $scope , $element , $window , $mdConstant , $mdTabInkRipple , $mdUtil ,
9
- $animateCss , $attrs , $compile , $mdTheming , $mdInteraction ,
9
+ $animateCss , $attrs , $compile , $mdTheming , $mdInteraction , $timeout ,
10
10
MdTabsPaginationService ) {
11
11
// define private properties
12
12
var ctrl = this ,
@@ -44,7 +44,8 @@ function MdTabsController ($scope, $element, $window, $mdConstant, $mdTabInkRipp
44
44
45
45
/**
46
46
* AngularJS Lifecycle hook for newer AngularJS versions.
47
- * Bindings are not guaranteed to have been assigned in the controller, but they are in the $onInit hook.
47
+ * Bindings are not guaranteed to have been assigned in the controller, but they are in the
48
+ * $onInit hook.
48
49
*/
49
50
function $onInit ( ) {
50
51
// Define one-way bindings
@@ -141,10 +142,10 @@ function MdTabsController ($scope, $element, $window, $mdConstant, $mdTabInkRipp
141
142
}
142
143
143
144
/**
144
- * Defines boolean attributes with default value set to true. (ie . md-stretch-tabs with no value
145
- * will be treated as being truthy)
146
- * @param key
147
- * @param handler
145
+ * Defines boolean attributes with default value set to true. I.e . md-stretch-tabs with no value
146
+ * will be treated as being truthy.
147
+ * @param { string } key
148
+ * @param { Function } handler
148
149
*/
149
150
function defineBooleanAttribute ( key , handler ) {
150
151
var attr = $attrs . $normalize ( 'md-' + key ) ;
@@ -167,19 +168,25 @@ function MdTabsController ($scope, $element, $window, $mdConstant, $mdTabInkRipp
167
168
// Change handlers
168
169
169
170
/**
170
- * Toggles stretch tabs class and updates inkbar when tab stretching changes
171
- * @param stretchTabs
171
+ * Toggles stretch tabs class and updates inkbar when tab stretching changes.
172
172
*/
173
- function handleStretchTabs ( stretchTabs ) {
173
+ function handleStretchTabs ( ) {
174
174
var elements = getElements ( ) ;
175
175
angular . element ( elements . wrapper ) . toggleClass ( 'md-stretch-tabs' , shouldStretchTabs ( ) ) ;
176
176
updateInkBarStyles ( ) ;
177
177
}
178
178
179
- function handleCenterTabs ( newValue ) {
179
+ /**
180
+ * Update the value of ctrl.shouldCenterTabs.
181
+ */
182
+ function handleCenterTabs ( ) {
180
183
ctrl . shouldCenterTabs = shouldCenterTabs ( ) ;
181
184
}
182
185
186
+ /**
187
+ * @param {number } newWidth new max tab width in pixels
188
+ * @param {number } oldWidth previous max tab width in pixels
189
+ */
183
190
function handleMaxTabWidth ( newWidth , oldWidth ) {
184
191
if ( newWidth !== oldWidth ) {
185
192
var elements = getElements ( ) ;
@@ -246,8 +253,8 @@ function MdTabsController ($scope, $element, $window, $mdConstant, $mdTabInkRipp
246
253
247
254
/**
248
255
* Update the UI whenever the selected index changes. Calls user-defined select/deselect methods.
249
- * @param newValue
250
- * @param oldValue
256
+ * @param { number } newValue selected index's new value
257
+ * @param { number } oldValue selected index's previous value
251
258
*/
252
259
function handleSelectedIndexChange ( newValue , oldValue ) {
253
260
if ( newValue === oldValue ) return ;
@@ -295,7 +302,7 @@ function MdTabsController ($scope, $element, $window, $mdConstant, $mdTabInkRipp
295
302
296
303
/**
297
304
* Handle user keyboard interactions
298
- * @param event
305
+ * @param { KeyboardEvent } event keydown event
299
306
*/
300
307
function keydown ( event ) {
301
308
switch ( event . keyCode ) {
@@ -384,21 +391,27 @@ function MdTabsController ($scope, $element, $window, $mdConstant, $mdTabInkRipp
384
391
} ) ;
385
392
}
386
393
394
+ /**
395
+ * Hides or shows the tabs ink bar.
396
+ * @param {boolean } hide A Boolean (not just truthy/falsy) value to determine whether the class
397
+ * should be added or removed.
398
+ */
387
399
function handleInkBar ( hide ) {
388
400
angular . element ( getElements ( ) . inkBar ) . toggleClass ( 'ng-hide' , hide ) ;
389
401
}
390
402
391
403
/**
392
- * Toggle dynamic height class when value changes
393
- * @param value
404
+ * Enables or disables tabs dynamic height.
405
+ * @param {boolean } value A Boolean (not just truthy/falsy) value to determine whether the class
406
+ * should be added or removed.
394
407
*/
395
408
function handleDynamicHeight ( value ) {
396
409
$element . toggleClass ( 'md-dynamic-height' , value ) ;
397
410
}
398
411
399
412
/**
400
413
* Remove a tab from the data and select the nearest valid tab.
401
- * @param tabData
414
+ * @param { Object } tabData tab to remove
402
415
*/
403
416
function removeTab ( tabData ) {
404
417
if ( destroyed ) return ;
@@ -419,8 +432,8 @@ function MdTabsController ($scope, $element, $window, $mdConstant, $mdTabInkRipp
419
432
420
433
/**
421
434
* Create an entry in the tabs array for a new tab at the specified index.
422
- * @param tabData
423
- * @param index
435
+ * @param { Object } tabData tab to insert
436
+ * @param { number } index location to insert the new tab
424
437
* @returns {* }
425
438
*/
426
439
function insertTab ( tabData , index ) {
@@ -583,9 +596,9 @@ function MdTabsController ($scope, $element, $window, $mdConstant, $mdTabInkRipp
583
596
/**
584
597
* Defines a property using a getter and setter in order to trigger a change handler without
585
598
* using `$watch` to observe changes.
586
- * @param key
587
- * @param handler
588
- * @param value
599
+ * @param { PropertyKey } key
600
+ * @param { Function } handler
601
+ * @param { any } value
589
602
*/
590
603
function defineProperty ( key , handler , value ) {
591
604
Object . defineProperty ( ctrl , key , {
@@ -608,12 +621,16 @@ function MdTabsController ($scope, $element, $window, $mdConstant, $mdTabInkRipp
608
621
609
622
/**
610
623
* Calculates the width of the pagination wrapper by summing the widths of the dummy tabs.
611
- * @returns {number }
624
+ * @returns {number } the width of the pagination wrapper in pixels
612
625
*/
613
626
function calcPagingWidth ( ) {
614
627
return calcTabsWidth ( getElements ( ) . tabs ) ;
615
628
}
616
629
630
+ /**
631
+ * @param {Array<HTMLElement> } tabs tab item elements for use in computing total width
632
+ * @returns {number } the width of the tabs in the specified array in pixels
633
+ */
617
634
function calcTabsWidth ( tabs ) {
618
635
var width = 0 ;
619
636
@@ -628,25 +645,35 @@ function MdTabsController ($scope, $element, $window, $mdConstant, $mdTabInkRipp
628
645
return Math . ceil ( width ) ;
629
646
}
630
647
631
- function getMaxTabWidth ( ) {
648
+ /**
649
+ * @returns {number } either the max width as constrained by the container or the max width from an
650
+ * old version of the Material Design spec.
651
+ * TODO update max tab width to equal the spec in 1.2.
652
+ */
653
+ function getMaxTabWidth ( ) {
632
654
var elements = getElements ( ) ,
633
- containerWidth = elements . canvas . clientWidth ,
655
+ containerWidth = elements . canvas . clientWidth ,
634
656
635
- // See https://material.google.com/ components/tabs.html#tabs-specs
636
- specMax = 264 ;
657
+ // See https://material.io/design/ components/tabs.html#spec which has been updated to 360px.
658
+ specMax = 264 ;
637
659
638
660
// Do the spec maximum, or the canvas width; whichever is *smaller* (tabs larger than the canvas
639
661
// width can break the pagination) but not less than 0
640
662
return Math . max ( 0 , Math . min ( containerWidth - 1 , specMax ) ) ;
641
663
}
642
664
665
+ /**
666
+ * @returns {number } the min width from an old version of the Material Design spec. This returns
667
+ * a larger min width if the container width is larger than 600px.
668
+ * TODO update min tab width to equal the spec in 1.2.
669
+ */
643
670
function getMinTabWidth ( ) {
644
671
var elements = getElements ( ) ,
645
- containerWidth = elements . canvas . clientWidth ,
646
- xsBreakpoint = 600 ,
672
+ containerWidth = elements . canvas . clientWidth ,
673
+ xsBreakpoint = 600 ,
647
674
648
- // See https://material.google.com/ components/tabs.html#tabs-specs
649
- specMin = containerWidth > xsBreakpoint ? 160 : 72 ;
675
+ // See https://material.io/design/ components/tabs.html#spec which has been updated to 90px.
676
+ specMin = containerWidth > xsBreakpoint ? 160 : 72 ;
650
677
651
678
// Do the spec minimum, or the canvas width; whichever is *smaller* (tabs larger than the canvas
652
679
// width can break the pagination) but not less than 0
@@ -668,8 +695,9 @@ function MdTabsController ($scope, $element, $window, $mdConstant, $mdTabInkRipp
668
695
}
669
696
670
697
/**
671
- * This moves the selected or focus index left or right. This is used by the keydown handler.
672
- * @param inc
698
+ * This moves the selected or focus index left or right. This is used by the keydown handler.
699
+ * @param {number } inc amount to increment
700
+ * @param {boolean } focus true to increment the focus index, false to increment the selected index
673
701
*/
674
702
function incrementIndex ( inc , focus ) {
675
703
var newIndex ,
@@ -687,7 +715,7 @@ function MdTabsController ($scope, $element, $window, $mdConstant, $mdTabInkRipp
687
715
}
688
716
689
717
/**
690
- * This is used to forward focus to tab container elements. This method is necessary to avoid
718
+ * This is used to forward focus to tab container elements. This method is necessary to avoid
691
719
* animation issues when attempting to focus an item that is out of view.
692
720
*/
693
721
function redirectFocus ( ) {
@@ -697,6 +725,7 @@ function MdTabsController ($scope, $element, $window, $mdConstant, $mdTabInkRipp
697
725
698
726
/**
699
727
* Forces the pagination to move the focused tab into view.
728
+ * @param {number } index of tab to have its offset adjusted
700
729
*/
701
730
function adjustOffset ( index ) {
702
731
var elements = getElements ( ) ;
@@ -728,7 +757,7 @@ function MdTabsController ($scope, $element, $window, $mdConstant, $mdTabInkRipp
728
757
}
729
758
730
759
/**
731
- * Iterates through all queued functions and clears the queue. This is used for functions that
760
+ * Iterates through all queued functions and clears the queue. This is used for functions that
732
761
* are called before the UI is ready, such as size calculations.
733
762
*/
734
763
function processQueue ( ) {
@@ -740,9 +769,10 @@ function MdTabsController ($scope, $element, $window, $mdConstant, $mdTabInkRipp
740
769
* Determines if the tab content area is needed.
741
770
*/
742
771
function updateHasContent ( ) {
743
- var hasContent = false ;
772
+ var hasContent = false ;
773
+ var i ;
744
774
745
- for ( var i = 0 ; i < ctrl . tabs . length ; i ++ ) {
775
+ for ( i = 0 ; i < ctrl . tabs . length ; i ++ ) {
746
776
if ( ctrl . tabs [ i ] . hasContent ) {
747
777
hasContent = true ;
748
778
break ;
@@ -784,7 +814,9 @@ function MdTabsController ($scope, $element, $window, $mdConstant, $mdTabInkRipp
784
814
currentHeight -= tabsHeight ;
785
815
newHeight -= tabsHeight ;
786
816
// Need to include bottom border in these calculations
787
- if ( $element . attr ( 'md-border-bottom' ) !== undefined ) ++ currentHeight ;
817
+ if ( $element . attr ( 'md-border-bottom' ) !== undefined ) {
818
+ ++ currentHeight ;
819
+ }
788
820
}
789
821
790
822
// Lock during animation so the user can't change tabs
@@ -825,20 +857,33 @@ function MdTabsController ($scope, $element, $window, $mdConstant, $mdTabInkRipp
825
857
826
858
/**
827
859
* Repositions the ink bar to the selected tab.
828
- * @returns {* }
829
- */
830
- function updateInkBarStyles ( ) {
860
+ * Parameters are used when calling itself recursively when md-center-tabs is used as we need to
861
+ * run two passes to properly center the tabs. These parameters ensure that we only run two passes
862
+ * and that we don't run indefinitely.
863
+ * @param {number= } previousTotalWidth previous width of pagination wrapper
864
+ * @param {number= } previousWidthOfTabItems previous width of all tab items
865
+ */
866
+ function updateInkBarStyles ( previousTotalWidth , previousWidthOfTabItems ) {
867
+ if ( ctrl . noInkBar ) {
868
+ return ;
869
+ }
831
870
var elements = getElements ( ) ;
832
871
833
872
if ( ! elements . tabs [ ctrl . selectedIndex ] ) {
834
873
angular . element ( elements . inkBar ) . css ( { left : 'auto' , right : 'auto' } ) ;
835
874
return ;
836
875
}
837
876
838
- if ( ! ctrl . tabs . length ) return queue . push ( ctrl . updateInkBarStyles ) ;
839
- // if the element is not visible, we will not be able to calculate sizes until it is
840
- // we should treat that as a resize event rather than just updating the ink bar
841
- if ( ! $element . prop ( 'offsetParent' ) ) return handleResizeWhenVisible ( ) ;
877
+ if ( ! ctrl . tabs . length ) {
878
+ queue . push ( ctrl . updateInkBarStyles ) ;
879
+ return ;
880
+ }
881
+ // If the element is not visible, we will not be able to calculate sizes until it becomes
882
+ // visible. We should treat that as a resize event rather than just updating the ink bar.
883
+ if ( ! $element . prop ( 'offsetParent' ) ) {
884
+ handleResizeWhenVisible ( ) ;
885
+ return ;
886
+ }
842
887
843
888
var index = ctrl . selectedIndex ,
844
889
totalWidth = elements . paging . offsetWidth ,
@@ -847,11 +892,14 @@ function MdTabsController ($scope, $element, $window, $mdConstant, $mdTabInkRipp
847
892
right = totalWidth - left - tab . offsetWidth ;
848
893
849
894
if ( ctrl . shouldCenterTabs ) {
850
- // We need to use the same calculate process as in the pagination wrapper, to avoid rounding deviations.
851
- var tabWidth = calcTabsWidth ( elements . tabs ) ;
852
-
853
- if ( totalWidth > tabWidth ) {
854
- $mdUtil . nextTick ( updateInkBarStyles , false ) ;
895
+ // We need to use the same calculate process as in the pagination wrapper, to avoid rounding
896
+ // deviations.
897
+ var totalWidthOfTabItems = calcTabsWidth ( elements . tabs ) ;
898
+
899
+ if ( totalWidth > totalWidthOfTabItems &&
900
+ previousTotalWidth !== totalWidth &&
901
+ previousWidthOfTabItems !== totalWidthOfTabItems ) {
902
+ $timeout ( updateInkBarStyles , 0 , true , totalWidth , totalWidthOfTabItems ) ;
855
903
}
856
904
}
857
905
updateInkBarClassName ( ) ;
0 commit comments