diff --git a/src/dev-app/select/select-demo.html b/src/dev-app/select/select-demo.html index abc808b618cd..21a0ab5f7d7e 100644 --- a/src/dev-app/select/select-demo.html +++ b/src/dev-app/select/select-demo.html @@ -222,7 +222,7 @@

Error message with errorStateMatcher

Pokemon - {{ creature.viewValue }} @@ -239,6 +239,10 @@

Error message with errorStateMatcher

+

+ + +

diff --git a/src/dev-app/select/select-demo.ts b/src/dev-app/select/select-demo.ts index 21469fbc7bd4..8f71bd6d7ce0 100644 --- a/src/dev-app/select/select-demo.ts +++ b/src/dev-app/select/select-demo.ts @@ -47,6 +47,7 @@ export class SelectDemo { topHeightCtrl = new FormControl(0); drinksTheme = 'primary'; pokemonTheme = 'primary'; + pokemonPanelHeight = 256; compareByValue = true; selectFormControl = new FormControl('', Validators.required); @@ -77,6 +78,19 @@ export class SelectDemo { {value: 'jigglypuff-4', viewValue: 'Jigglypuff with a really long name that will truncate'}, {value: 'ditto-5', viewValue: 'Ditto'}, {value: 'psyduck-6', viewValue: 'Psyduck'}, + {value: 'caterpie-7', viewValue: 'Caterpie'}, + {value: 'weedle-8', viewValue: 'Weedle'}, + {value: 'pidgey-9', viewValue: 'Pidgey'}, + {value: 'rattata-10', viewValue: 'Rattata'}, + {value: 'spearow-11', viewValue: 'Spearow'}, + {value: 'ekans-12', viewValue: 'Ekans'}, + {value: 'sandshrew-13', viewValue: 'Sandshrew'}, + {value: 'nidoran-14', viewValue: 'Nidoran'}, + {value: 'clefairy-15', viewValue: 'Clefairy'}, + {value: 'vulpix-16', viewValue: 'Vulpix'}, + {value: 'zubat-17', viewValue: 'Zubat'}, + {value: 'oddish-18', viewValue: 'Oddish'}, + {value: 'paras-19', viewValue: 'Paras'}, ]; availableThemes = [ diff --git a/src/material/select/select.scss b/src/material/select/select.scss index ef021f3cac73..d64a6814f4f8 100644 --- a/src/material/select/select.scss +++ b/src/material/select/select.scss @@ -6,7 +6,6 @@ $mat-select-arrow-size: 5px !default; $mat-select-arrow-margin: 4px !default; -$mat-select-panel-max-height: 256px !default; $mat-select-item-height: 3em !default; $mat-select-placeholder-arrow-space: 2 * ($mat-select-arrow-size + $mat-select-arrow-margin); @@ -93,7 +92,6 @@ $mat-select-placeholder-arrow-space: 2 * ($mat-select-arrow-size + $mat-select-a @include mat-menu-base(); padding-top: 0; padding-bottom: 0; - max-height: $mat-select-panel-max-height; min-width: 100%; // prevents some animation twitching and test inconsistencies in IE11 border-radius: 4px; diff --git a/src/material/select/select.ts b/src/material/select/select.ts index f8875435c053..097abd42fab7 100644 --- a/src/material/select/select.ts +++ b/src/material/select/select.ts @@ -8,7 +8,7 @@ import {ActiveDescendantKeyManager, LiveAnnouncer} from '@angular/cdk/a11y'; import {Directionality} from '@angular/cdk/bidi'; -import {coerceBooleanProperty} from '@angular/cdk/coercion'; +import {coerceBooleanProperty, coerceNumberProperty} from '@angular/cdk/coercion'; import {SelectionModel} from '@angular/cdk/collections'; import { A, @@ -101,9 +101,6 @@ let nextUniqueId = 0; * the trigger element. */ -/** The max height of the select's overlay panel */ -export const SELECT_PANEL_MAX_HEIGHT = 256; - /** The panel's padding on the x-axis */ export const SELECT_PANEL_PADDING_X = 16; @@ -238,6 +235,14 @@ export class MatSelect extends _MatSelectMixinBase implements AfterContentInit, /** The scroll position of the overlay panel, calculated to center the selected option. */ private _scrollTop = 0; + /** + * The max height of the select's overlay panel + * 256 value is define has follow: + * - display 5 select options of 48 px (240px) + * - remaining 16px help the user to see if other options are available + */ + private _panelMaxHeight = 256; + /** The placeholder displayed in the trigger of the select. */ private _placeholder: string; @@ -388,6 +393,13 @@ export class MatSelect extends _MatSelectMixinBase implements AfterContentInit, this._disableOptionCentering = coerceBooleanProperty(value); } + /** Define select panel maximum height. */ + @Input() + get panelMaxHeight(): number { return this._panelMaxHeight; } + set panelMaxHeight(value: number) { + this._panelMaxHeight = coerceNumberProperty(value) || this._panelMaxHeight; + } + /** * Function to compare the option values with the selected values. The first argument * is a value from an option. The second is a value from the selection. A boolean @@ -805,6 +817,9 @@ export class MatSelect extends _MatSelectMixinBase implements AfterContentInit, */ _onAttached(): void { this.overlayDir.positionChange.pipe(take(1)).subscribe(() => { + // Set panel max-height dynamically after creation. + this.panel.nativeElement.style.maxHeight = `${this._panelMaxHeight}px`; + this._setPseudoCheckboxPaddingSize(); this._changeDetectorRef.detectChanges(); this._calculateOverlayOffsetX(); @@ -1037,7 +1052,7 @@ export class MatSelect extends _MatSelectMixinBase implements AfterContentInit, activeOptionIndex + labelCount, this._getItemHeight(), this.panel.nativeElement.scrollTop, - SELECT_PANEL_MAX_HEIGHT + this._panelMaxHeight ); } @@ -1057,7 +1072,7 @@ export class MatSelect extends _MatSelectMixinBase implements AfterContentInit, private _calculateOverlayPosition(): void { const itemHeight = this._getItemHeight(); const items = this._getItemCount(); - const panelHeight = Math.min(items * itemHeight, SELECT_PANEL_MAX_HEIGHT); + const panelHeight = Math.min(items * itemHeight, this._panelMaxHeight); const scrollContainerHeight = items * itemHeight; // The farthest the panel can be scrolled before it hits the bottom @@ -1188,7 +1203,7 @@ export class MatSelect extends _MatSelectMixinBase implements AfterContentInit, maxScroll: number): number { const itemHeight = this._getItemHeight(); const optionHeightAdjustment = (itemHeight - this._triggerRect.height) / 2; - const maxOptionsDisplayed = Math.floor(SELECT_PANEL_MAX_HEIGHT / itemHeight); + const maxOptionsDisplayed = Math.floor(this._panelMaxHeight / itemHeight); let optionOffsetFromPanelTop: number; // Disable offset if requested by user by returning 0 as value to offset @@ -1204,8 +1219,8 @@ export class MatSelect extends _MatSelectMixinBase implements AfterContentInit, // The first item is partially out of the viewport. Therefore we need to calculate what // portion of it is shown in the viewport and account for it in our offset. - let partialItemHeight = - itemHeight - (this._getItemCount() * itemHeight - SELECT_PANEL_MAX_HEIGHT) % itemHeight; + let partialItemHeight = itemHeight - + (this._getItemCount() * itemHeight - this._panelMaxHeight) % itemHeight; // Because the panel height is longer than the height of the options alone, // there is always extra padding at the top or bottom of the panel. When @@ -1241,7 +1256,7 @@ export class MatSelect extends _MatSelectMixinBase implements AfterContentInit, const panelHeightTop = Math.abs(this._offsetY); const totalPanelHeight = - Math.min(this._getItemCount() * itemHeight, SELECT_PANEL_MAX_HEIGHT); + Math.min(this._getItemCount() * itemHeight, this._panelMaxHeight); const panelHeightBottom = totalPanelHeight - panelHeightTop - this._triggerRect.height; if (panelHeightBottom > bottomSpaceAvailable) {