@@ -64,31 +64,18 @@ let nextUniqueId = 0;
64
64
* the trigger element.
65
65
*/
66
66
67
- /** The fixed height of every option element (option, group header etc.). */
68
- export const SELECT_ITEM_HEIGHT = 48 ;
69
-
70
67
/** The max height of the select's overlay panel */
71
68
export const SELECT_PANEL_MAX_HEIGHT = 256 ;
72
69
73
- /** The max number of options visible at once in the select panel. */
74
- export const SELECT_MAX_OPTIONS_DISPLAYED =
75
- Math . floor ( SELECT_PANEL_MAX_HEIGHT / SELECT_ITEM_HEIGHT ) ;
76
-
77
- /** The fixed height of the select's trigger element. */
78
- export const SELECT_TRIGGER_HEIGHT = 30 ;
79
-
80
- /**
81
- * Must adjust for the difference in height between the option and the trigger,
82
- * so the text will align on the y axis.
83
- */
84
- export const SELECT_OPTION_HEIGHT_ADJUSTMENT = ( SELECT_ITEM_HEIGHT - SELECT_TRIGGER_HEIGHT ) / 2 ;
85
-
86
70
/** The panel's padding on the x-axis */
87
71
export const SELECT_PANEL_PADDING_X = 16 ;
88
72
89
73
/** The panel's x axis padding if it is indented (e.g. there is an option group). */
90
74
export const SELECT_PANEL_INDENT_PADDING_X = SELECT_PANEL_PADDING_X * 2 ;
91
75
76
+ /** The height of the select items in `em` units. */
77
+ export const SELECT_ITEM_HEIGHT_EM = 3 ;
78
+
92
79
/**
93
80
* Distance between the panel edge and the option text in
94
81
* multi-selection mode.
@@ -223,6 +210,12 @@ export class MdSelect extends _MdSelectMixinBase implements AfterContentInit, On
223
210
/** Tab index for the element. */
224
211
private _tabIndex : number ;
225
212
213
+ /** The cached height of the trigger element. */
214
+ private _triggerHeight : number ;
215
+
216
+ /** The cached font-size of the trigger element. */
217
+ _triggerFontSize = 0 ;
218
+
226
219
/** Deals with the selection logic. */
227
220
_selectionModel : SelectionModel < MdOption > ;
228
221
@@ -297,6 +290,9 @@ export class MdSelect extends _MdSelectMixinBase implements AfterContentInit, On
297
290
/** Trigger that opens the select. */
298
291
@ViewChild ( 'trigger' ) trigger : ElementRef ;
299
292
293
+ /** Element used to measure the font-size of the trigger element. */
294
+ @ViewChild ( 'measureFontSize' ) _measureFontSizeEl : ElementRef ;
295
+
300
296
/** Overlay pane containing the options. */
301
297
@ViewChild ( ConnectedOverlayDirective ) overlayDir : ConnectedOverlayDirective ;
302
298
@@ -866,31 +862,29 @@ export class MdSelect extends _MdSelectMixinBase implements AfterContentInit, On
866
862
867
863
/** Calculates the scroll position and x- and y-offsets of the overlay panel. */
868
864
private _calculateOverlayPosition ( ) : void {
865
+ this . _triggerHeight = this . trigger . nativeElement . getBoundingClientRect ( ) . height ;
866
+ this . _triggerFontSize = this . _measureFontSizeEl . nativeElement . getBoundingClientRect ( ) . height ;
867
+
868
+ const itemHeight = this . _triggerFontSize * SELECT_ITEM_HEIGHT_EM ;
869
+
869
870
const items = this . _getItemCount ( ) ;
870
- const panelHeight = Math . min ( items * SELECT_ITEM_HEIGHT , SELECT_PANEL_MAX_HEIGHT ) ;
871
- const scrollContainerHeight = items * SELECT_ITEM_HEIGHT ;
871
+ const panelHeight = Math . min ( items * itemHeight , SELECT_PANEL_MAX_HEIGHT ) ;
872
+ const scrollContainerHeight = items * itemHeight ;
872
873
873
874
// The farthest the panel can be scrolled before it hits the bottom
874
875
const maxScroll = scrollContainerHeight - panelHeight ;
875
876
876
- if ( ! this . empty ) {
877
- let selectedOptionOffset = this . _getOptionIndex ( this . _selectionModel . selected [ 0 ] ) ! ;
877
+ // If no value is selected we open the popup to the first item.
878
+ let selectedOptionOffset =
879
+ this . empty ? 0 : this . _getOptionIndex ( this . _selectionModel . selected [ 0 ] ) ! ;
878
880
879
- selectedOptionOffset += this . _getLabelCountBeforeOption ( selectedOptionOffset ) ;
881
+ selectedOptionOffset += this . _getLabelCountBeforeOption ( selectedOptionOffset ) ;
880
882
881
- // We must maintain a scroll buffer so the selected option will be scrolled to the
882
- // center of the overlay panel rather than the top.
883
- const scrollBuffer = panelHeight / 2 ;
884
- this . _scrollTop = this . _calculateOverlayScroll ( selectedOptionOffset , scrollBuffer , maxScroll ) ;
885
- this . _offsetY = this . _calculateOverlayOffsetY ( selectedOptionOffset , scrollBuffer , maxScroll ) ;
886
- } else {
887
- // If no option is selected, the panel centers on the first option. In this case,
888
- // we must only adjust for the height difference between the option element
889
- // and the trigger element, then multiply it by -1 to ensure the panel moves
890
- // in the correct direction up the page.
891
- this . _offsetY = ( SELECT_ITEM_HEIGHT - SELECT_TRIGGER_HEIGHT ) / 2 * - 1 -
892
- ( this . _getLabelCountBeforeOption ( 0 ) * SELECT_ITEM_HEIGHT ) ;
893
- }
883
+ // We must maintain a scroll buffer so the selected option will be scrolled to the
884
+ // center of the overlay panel rather than the top.
885
+ const scrollBuffer = panelHeight / 2 ;
886
+ this . _scrollTop = this . _calculateOverlayScroll ( selectedOptionOffset , scrollBuffer , maxScroll ) ;
887
+ this . _offsetY = this . _calculateOverlayOffsetY ( selectedOptionOffset , scrollBuffer , maxScroll ) ;
894
888
895
889
this . _checkOverlayWithinViewport ( maxScroll ) ;
896
890
}
@@ -904,8 +898,9 @@ export class MdSelect extends _MdSelectMixinBase implements AfterContentInit, On
904
898
*/
905
899
_calculateOverlayScroll ( selectedIndex : number , scrollBuffer : number ,
906
900
maxScroll : number ) : number {
907
- const optionOffsetFromScrollTop = SELECT_ITEM_HEIGHT * selectedIndex ;
908
- const halfOptionHeight = SELECT_ITEM_HEIGHT / 2 ;
901
+ const itemHeight = this . _triggerFontSize * SELECT_ITEM_HEIGHT_EM ;
902
+ const optionOffsetFromScrollTop = itemHeight * selectedIndex ;
903
+ const halfOptionHeight = itemHeight / 2 ;
909
904
910
905
// Starts at the optionOffsetFromScrollTop, which scrolls the option to the top of the
911
906
// scroll container, then subtracts the scroll buffer to scroll the option down to
@@ -975,31 +970,34 @@ export class MdSelect extends _MdSelectMixinBase implements AfterContentInit, On
975
970
*/
976
971
private _calculateOverlayOffsetY ( selectedIndex : number , scrollBuffer : number ,
977
972
maxScroll : number ) : number {
973
+ const itemHeight = this . _triggerFontSize * SELECT_ITEM_HEIGHT_EM ;
974
+ const optionHeightAdjustment = ( itemHeight - this . _triggerHeight ) / 2 ;
975
+ const maxOptionsDisplayed = Math . floor ( SELECT_PANEL_MAX_HEIGHT / itemHeight ) ;
978
976
let optionOffsetFromPanelTop : number ;
979
977
980
978
if ( this . _scrollTop === 0 ) {
981
- optionOffsetFromPanelTop = selectedIndex * SELECT_ITEM_HEIGHT ;
979
+ optionOffsetFromPanelTop = selectedIndex * itemHeight ;
982
980
} else if ( this . _scrollTop === maxScroll ) {
983
- const firstDisplayedIndex = this . _getItemCount ( ) - SELECT_MAX_OPTIONS_DISPLAYED ;
981
+ const firstDisplayedIndex = this . _getItemCount ( ) - maxOptionsDisplayed ;
984
982
const selectedDisplayIndex = selectedIndex - firstDisplayedIndex ;
985
983
986
984
// Because the panel height is longer than the height of the options alone,
987
985
// there is always extra padding at the top or bottom of the panel. When
988
986
// scrolled to the very bottom, this padding is at the top of the panel and
989
987
// must be added to the offset.
990
988
optionOffsetFromPanelTop =
991
- selectedDisplayIndex * SELECT_ITEM_HEIGHT + SELECT_PANEL_PADDING_Y ;
989
+ selectedDisplayIndex * itemHeight + SELECT_PANEL_PADDING_Y ;
992
990
} else {
993
991
// If the option was scrolled to the middle of the panel using a scroll buffer,
994
992
// its offset will be the scroll buffer minus the half height that was added to
995
993
// center it.
996
- optionOffsetFromPanelTop = scrollBuffer - SELECT_ITEM_HEIGHT / 2 ;
994
+ optionOffsetFromPanelTop = scrollBuffer - itemHeight / 2 ;
997
995
}
998
996
999
997
// The final offset is the option's offset from the top, adjusted for the height
1000
998
// difference, multiplied by -1 to ensure that the overlay moves in the correct
1001
999
// direction up the page.
1002
- return optionOffsetFromPanelTop * - 1 - SELECT_OPTION_HEIGHT_ADJUSTMENT ;
1000
+ return optionOffsetFromPanelTop * - 1 - optionHeightAdjustment ;
1003
1001
}
1004
1002
1005
1003
/**
@@ -1009,6 +1007,7 @@ export class MdSelect extends _MdSelectMixinBase implements AfterContentInit, On
1009
1007
* sets the offset back to 0 to allow the fallback position to take over.
1010
1008
*/
1011
1009
private _checkOverlayWithinViewport ( maxScroll : number ) : void {
1010
+ const itemHeight = this . _triggerFontSize * SELECT_ITEM_HEIGHT_EM ;
1012
1011
const viewportRect = this . _viewportRuler . getViewportRect ( ) ;
1013
1012
const triggerRect = this . _getTriggerRect ( ) ;
1014
1013
@@ -1018,7 +1017,7 @@ export class MdSelect extends _MdSelectMixinBase implements AfterContentInit, On
1018
1017
1019
1018
const panelHeightTop = Math . abs ( this . _offsetY ) ;
1020
1019
const totalPanelHeight =
1021
- Math . min ( this . _getItemCount ( ) * SELECT_ITEM_HEIGHT , SELECT_PANEL_MAX_HEIGHT ) ;
1020
+ Math . min ( this . _getItemCount ( ) * itemHeight , SELECT_PANEL_MAX_HEIGHT ) ;
1022
1021
const panelHeightBottom = totalPanelHeight - panelHeightTop - triggerRect . height ;
1023
1022
1024
1023
if ( panelHeightBottom > bottomSpaceAvailable ) {
@@ -1074,8 +1073,9 @@ export class MdSelect extends _MdSelectMixinBase implements AfterContentInit, On
1074
1073
1075
1074
/** Sets the transform origin point based on the selected option. */
1076
1075
private _getOriginBasedOnOption ( ) : string {
1077
- const originY =
1078
- Math . abs ( this . _offsetY ) - SELECT_OPTION_HEIGHT_ADJUSTMENT + SELECT_ITEM_HEIGHT / 2 ;
1076
+ const itemHeight = this . _triggerFontSize * SELECT_ITEM_HEIGHT_EM ;
1077
+ const optionHeightAdjustment = ( itemHeight - this . _triggerHeight ) / 2 ;
1078
+ const originY = Math . abs ( this . _offsetY ) - optionHeightAdjustment + itemHeight / 2 ;
1079
1079
return `50% ${ originY } px 0px` ;
1080
1080
}
1081
1081
0 commit comments