@@ -78,31 +78,18 @@ let nextUniqueId = 0;
78
78
* the trigger element.
79
79
*/
80
80
81
- /** The fixed height of every option element (option, group header etc.). */
82
- export const SELECT_ITEM_HEIGHT = 48 ;
83
-
84
81
/** The max height of the select's overlay panel */
85
82
export const SELECT_PANEL_MAX_HEIGHT = 256 ;
86
83
87
- /** The max number of options visible at once in the select panel. */
88
- export const SELECT_MAX_OPTIONS_DISPLAYED =
89
- Math . floor ( SELECT_PANEL_MAX_HEIGHT / SELECT_ITEM_HEIGHT ) ;
90
-
91
- /** The fixed height of the select's trigger element. */
92
- export const SELECT_TRIGGER_HEIGHT = 30 ;
93
-
94
- /**
95
- * Must adjust for the difference in height between the option and the trigger,
96
- * so the text will align on the y axis.
97
- */
98
- export const SELECT_OPTION_HEIGHT_ADJUSTMENT = ( SELECT_ITEM_HEIGHT - SELECT_TRIGGER_HEIGHT ) / 2 ;
99
-
100
84
/** The panel's padding on the x-axis */
101
85
export const SELECT_PANEL_PADDING_X = 16 ;
102
86
103
87
/** The panel's x axis padding if it is indented (e.g. there is an option group). */
104
88
export const SELECT_PANEL_INDENT_PADDING_X = SELECT_PANEL_PADDING_X * 2 ;
105
89
90
+ /** The height of the select items in `em` units. */
91
+ export const SELECT_ITEM_HEIGHT_EM = 3 ;
92
+
106
93
/**
107
94
* Distance between the panel edge and the option text in
108
95
* multi-selection mode.
@@ -239,6 +226,12 @@ export class MdSelect extends _MdSelectMixinBase implements AfterContentInit, On
239
226
/** Unique id for this input. */
240
227
private _uid = `mat-select-${ nextUniqueId ++ } ` ;
241
228
229
+ /** The cached height of the trigger element. */
230
+ private _triggerHeight : number ;
231
+
232
+ /** The cached font-size of the trigger element. */
233
+ _triggerFontSize = 0 ;
234
+
242
235
/** Deals with the selection logic. */
243
236
_selectionModel : SelectionModel < MdOption > ;
244
237
@@ -313,6 +306,9 @@ export class MdSelect extends _MdSelectMixinBase implements AfterContentInit, On
313
306
/** Trigger that opens the select. */
314
307
@ViewChild ( 'trigger' ) trigger : ElementRef ;
315
308
309
+ /** Element used to measure the font-size of the trigger element. */
310
+ @ViewChild ( 'measureFontSize' ) _measureFontSizeEl : ElementRef ;
311
+
316
312
/** Overlay pane containing the options. */
317
313
@ViewChild ( ConnectedOverlayDirective ) overlayDir : ConnectedOverlayDirective ;
318
314
@@ -684,7 +680,7 @@ export class MdSelect extends _MdSelectMixinBase implements AfterContentInit, On
684
680
// Defer setting the value in order to avoid the "Expression
685
681
// has changed after it was checked" errors from Angular.
686
682
Promise . resolve ( ) . then ( ( ) => {
687
- this . _setSelectionByValue ( this . _control ? this . _control . value : this . _value ) ;
683
+ this . _setSelectionByValue ( this . ngControl ? this . ngControl . value : this . _value ) ;
688
684
} ) ;
689
685
}
690
686
@@ -899,34 +895,30 @@ export class MdSelect extends _MdSelectMixinBase implements AfterContentInit, On
899
895
900
896
/** Calculates the scroll position and x- and y-offsets of the overlay panel. */
901
897
private _calculateOverlayPosition ( ) : void {
898
+ this . _triggerHeight = this . trigger . nativeElement . getBoundingClientRect ( ) . height ;
899
+ this . _triggerFontSize = this . _measureFontSizeEl . nativeElement . getBoundingClientRect ( ) . height ;
900
+
901
+ const itemHeight = this . _triggerFontSize * SELECT_ITEM_HEIGHT_EM ;
902
+
902
903
const items = this . _getItemCount ( ) ;
903
- const panelHeight = Math . min ( items * SELECT_ITEM_HEIGHT , SELECT_PANEL_MAX_HEIGHT ) ;
904
- const scrollContainerHeight = items * SELECT_ITEM_HEIGHT ;
904
+ const panelHeight = Math . min ( items * itemHeight , SELECT_PANEL_MAX_HEIGHT ) ;
905
+ const scrollContainerHeight = items * itemHeight ;
905
906
906
907
// The farthest the panel can be scrolled before it hits the bottom
907
908
const maxScroll = scrollContainerHeight - panelHeight ;
908
909
909
- if ( ! this . empty ) {
910
- let selectedOptionOffset = this . _getOptionIndex ( this . _selectionModel . selected [ 0 ] ) ! ;
910
+ // If no value is selected we open the popup to the first item.
911
+ let selectedOptionOffset =
912
+ this . empty ? 0 : this . _getOptionIndex ( this . _selectionModel . selected [ 0 ] ) ! ;
911
913
912
- selectedOptionOffset += MdOption . countGroupLabelsBeforeOption ( selectedOptionOffset ,
913
- this . options , this . optionGroups ) ;
914
+ selectedOptionOffset += MdOption . countGroupLabelsBeforeOption ( selectedOptionOffset ,
915
+ this . options , this . optionGroups ) ;
914
916
915
- // We must maintain a scroll buffer so the selected option will be scrolled to the
916
- // center of the overlay panel rather than the top.
917
- const scrollBuffer = panelHeight / 2 ;
918
- this . _scrollTop = this . _calculateOverlayScroll ( selectedOptionOffset , scrollBuffer , maxScroll ) ;
919
- this . _offsetY = this . _calculateOverlayOffsetY ( selectedOptionOffset , scrollBuffer , maxScroll ) ;
920
- } else {
921
- // If no option is selected, the panel centers on the first option. In this case,
922
- // we must only adjust for the height difference between the option element
923
- // and the trigger element, then multiply it by -1 to ensure the panel moves
924
- // in the correct direction up the page.
925
- let groupLabels = MdOption . countGroupLabelsBeforeOption ( 0 , this . options , this . optionGroups ) ;
926
-
927
- this . _offsetY = ( SELECT_ITEM_HEIGHT - SELECT_TRIGGER_HEIGHT ) / 2 * - 1 -
928
- ( groupLabels * SELECT_ITEM_HEIGHT ) ;
929
- }
917
+ // We must maintain a scroll buffer so the selected option will be scrolled to the
918
+ // center of the overlay panel rather than the top.
919
+ const scrollBuffer = panelHeight / 2 ;
920
+ this . _scrollTop = this . _calculateOverlayScroll ( selectedOptionOffset , scrollBuffer , maxScroll ) ;
921
+ this . _offsetY = this . _calculateOverlayOffsetY ( selectedOptionOffset , scrollBuffer , maxScroll ) ;
930
922
931
923
this . _checkOverlayWithinViewport ( maxScroll ) ;
932
924
}
@@ -940,8 +932,9 @@ export class MdSelect extends _MdSelectMixinBase implements AfterContentInit, On
940
932
*/
941
933
_calculateOverlayScroll ( selectedIndex : number , scrollBuffer : number ,
942
934
maxScroll : number ) : number {
943
- const optionOffsetFromScrollTop = SELECT_ITEM_HEIGHT * selectedIndex ;
944
- const halfOptionHeight = SELECT_ITEM_HEIGHT / 2 ;
935
+ const itemHeight = this . _triggerFontSize * SELECT_ITEM_HEIGHT_EM ;
936
+ const optionOffsetFromScrollTop = itemHeight * selectedIndex ;
937
+ const halfOptionHeight = itemHeight / 2 ;
945
938
946
939
// Starts at the optionOffsetFromScrollTop, which scrolls the option to the top of the
947
940
// scroll container, then subtracts the scroll buffer to scroll the option down to
@@ -1011,31 +1004,34 @@ export class MdSelect extends _MdSelectMixinBase implements AfterContentInit, On
1011
1004
*/
1012
1005
private _calculateOverlayOffsetY ( selectedIndex : number , scrollBuffer : number ,
1013
1006
maxScroll : number ) : number {
1007
+ const itemHeight = this . _triggerFontSize * SELECT_ITEM_HEIGHT_EM ;
1008
+ const optionHeightAdjustment = ( itemHeight - this . _triggerHeight ) / 2 ;
1009
+ const maxOptionsDisplayed = Math . floor ( SELECT_PANEL_MAX_HEIGHT / itemHeight ) ;
1014
1010
let optionOffsetFromPanelTop : number ;
1015
1011
1016
1012
if ( this . _scrollTop === 0 ) {
1017
- optionOffsetFromPanelTop = selectedIndex * SELECT_ITEM_HEIGHT ;
1013
+ optionOffsetFromPanelTop = selectedIndex * itemHeight ;
1018
1014
} else if ( this . _scrollTop === maxScroll ) {
1019
- const firstDisplayedIndex = this . _getItemCount ( ) - SELECT_MAX_OPTIONS_DISPLAYED ;
1015
+ const firstDisplayedIndex = this . _getItemCount ( ) - maxOptionsDisplayed ;
1020
1016
const selectedDisplayIndex = selectedIndex - firstDisplayedIndex ;
1021
1017
1022
1018
// Because the panel height is longer than the height of the options alone,
1023
1019
// there is always extra padding at the top or bottom of the panel. When
1024
1020
// scrolled to the very bottom, this padding is at the top of the panel and
1025
1021
// must be added to the offset.
1026
1022
optionOffsetFromPanelTop =
1027
- selectedDisplayIndex * SELECT_ITEM_HEIGHT + SELECT_PANEL_PADDING_Y ;
1023
+ selectedDisplayIndex * itemHeight + SELECT_PANEL_PADDING_Y ;
1028
1024
} else {
1029
1025
// If the option was scrolled to the middle of the panel using a scroll buffer,
1030
1026
// its offset will be the scroll buffer minus the half height that was added to
1031
1027
// center it.
1032
- optionOffsetFromPanelTop = scrollBuffer - SELECT_ITEM_HEIGHT / 2 ;
1028
+ optionOffsetFromPanelTop = scrollBuffer - itemHeight / 2 ;
1033
1029
}
1034
1030
1035
1031
// The final offset is the option's offset from the top, adjusted for the height
1036
1032
// difference, multiplied by -1 to ensure that the overlay moves in the correct
1037
1033
// direction up the page.
1038
- return optionOffsetFromPanelTop * - 1 - SELECT_OPTION_HEIGHT_ADJUSTMENT ;
1034
+ return optionOffsetFromPanelTop * - 1 - optionHeightAdjustment ;
1039
1035
}
1040
1036
1041
1037
/**
@@ -1045,6 +1041,7 @@ export class MdSelect extends _MdSelectMixinBase implements AfterContentInit, On
1045
1041
* sets the offset back to 0 to allow the fallback position to take over.
1046
1042
*/
1047
1043
private _checkOverlayWithinViewport ( maxScroll : number ) : void {
1044
+ const itemHeight = this . _triggerFontSize * SELECT_ITEM_HEIGHT_EM ;
1048
1045
const viewportRect = this . _viewportRuler . getViewportRect ( ) ;
1049
1046
const triggerRect = this . _getTriggerRect ( ) ;
1050
1047
@@ -1054,7 +1051,7 @@ export class MdSelect extends _MdSelectMixinBase implements AfterContentInit, On
1054
1051
1055
1052
const panelHeightTop = Math . abs ( this . _offsetY ) ;
1056
1053
const totalPanelHeight =
1057
- Math . min ( this . _getItemCount ( ) * SELECT_ITEM_HEIGHT , SELECT_PANEL_MAX_HEIGHT ) ;
1054
+ Math . min ( this . _getItemCount ( ) * itemHeight , SELECT_PANEL_MAX_HEIGHT ) ;
1058
1055
const panelHeightBottom = totalPanelHeight - panelHeightTop - triggerRect . height ;
1059
1056
1060
1057
if ( panelHeightBottom > bottomSpaceAvailable ) {
@@ -1110,8 +1107,9 @@ export class MdSelect extends _MdSelectMixinBase implements AfterContentInit, On
1110
1107
1111
1108
/** Sets the transform origin point based on the selected option. */
1112
1109
private _getOriginBasedOnOption ( ) : string {
1113
- const originY =
1114
- Math . abs ( this . _offsetY ) - SELECT_OPTION_HEIGHT_ADJUSTMENT + SELECT_ITEM_HEIGHT / 2 ;
1110
+ const itemHeight = this . _triggerFontSize * SELECT_ITEM_HEIGHT_EM ;
1111
+ const optionHeightAdjustment = ( itemHeight - this . _triggerHeight ) / 2 ;
1112
+ const originY = Math . abs ( this . _offsetY ) - optionHeightAdjustment + itemHeight / 2 ;
1115
1113
return `50% ${ originY } px 0px` ;
1116
1114
}
1117
1115
0 commit comments