@@ -82,7 +82,7 @@ class SelectListRenderer implements IRenderer<ISelectOptionItem, ISelectListTemp
8282export class SelectBoxList implements ISelectBoxDelegate , IVirtualDelegate < ISelectOptionItem > {
8383
8484 private static readonly DEFAULT_DROPDOWN_MINIMUM_BOTTOM_MARGIN = 32 ;
85- private static readonly DEFAULT_DROPDOWN_MINIMUM_TOP_MARGIN = 42 ;
85+ private static readonly DEFAULT_DROPDOWN_MINIMUM_TOP_MARGIN = 2 ;
8686 private static readonly DEFAULT_MINIMUM_VISIBLE_OPTIONS = 3 ;
8787
8888 private _isVisible : boolean ;
@@ -393,9 +393,11 @@ export class SelectBoxList implements ISelectBoxDelegate, IVirtualDelegate<ISele
393393 }
394394
395395 // Set drop-down position above/below from required height and margins
396- this . layoutSelectDropDown ( true ) ;
396+ // If pre-layout cannot fit at least one option do not show drop-down
397+ if ( ! this . layoutSelectDropDown ( true ) ) {
398+ return ;
399+ }
397400
398- this . _isVisible = true ;
399401 this . cloneElementFont ( this . selectElement , this . selectDropDownContainer ) ;
400402
401403 this . contextViewProvider . showContextView ( {
@@ -411,6 +413,7 @@ export class SelectBoxList implements ISelectBoxDelegate, IVirtualDelegate<ISele
411413
412414 // Track initial selection the case user escape, blur
413415 this . _currentSelection = this . selected ;
416+ this . _isVisible = true ;
414417 }
415418
416419 private hideSelectDropDown ( focusSelect : boolean ) {
@@ -423,6 +426,7 @@ export class SelectBoxList implements ISelectBoxDelegate, IVirtualDelegate<ISele
423426 if ( focusSelect ) {
424427 this . selectElement . focus ( ) ;
425428 }
429+
426430 this . contextViewProvider . hideContextView ( ) ;
427431 }
428432
@@ -443,7 +447,7 @@ export class SelectBoxList implements ISelectBoxDelegate, IVirtualDelegate<ISele
443447 } ;
444448 }
445449
446- private layoutSelectDropDown ( preLayoutPosition ?: boolean ) {
450+ private layoutSelectDropDown ( preLayoutPosition ?: boolean ) : boolean {
447451
448452 // Layout ContextView drop down select list and container
449453 // Have to manage our vertical overflow, sizing, position below or above
@@ -454,30 +458,54 @@ export class SelectBoxList implements ISelectBoxDelegate, IVirtualDelegate<ISele
454458 const selectPosition = dom . getDomNodePagePosition ( this . selectElement ) ;
455459 const styles = getComputedStyle ( this . selectElement ) ;
456460 const verticalPadding = parseFloat ( styles . getPropertyValue ( '--dropdown-padding-top' ) ) + parseFloat ( styles . getPropertyValue ( '--dropdown-padding-bottom' ) ) ;
457- let maxSelectDropDownHeight = 0 ;
458- maxSelectDropDownHeight = ( window . innerHeight - selectPosition . top - selectPosition . height - this . selectBoxOptions . minBottomMargin ) ;
461+ const maxSelectDropDownHeightBelow = ( window . innerHeight - selectPosition . top - selectPosition . height - this . selectBoxOptions . minBottomMargin ) ;
462+ const maxSelectDropDownHeightAbove = ( selectPosition . top - SelectBoxList . DEFAULT_DROPDOWN_MINIMUM_TOP_MARGIN ) ;
459463
464+ // Get initial list height and determine space above and below
460465 this . selectList . layout ( ) ;
461466 let listHeight = this . selectList . contentHeight ;
467+ const minRequiredDropDownHeight = listHeight + verticalPadding ;
468+ const maxVisibleOptionsBelow = ( ( Math . floor ( ( maxSelectDropDownHeightBelow - verticalPadding ) / this . getHeight ( ) ) ) ) ;
469+ const maxVisibleOptionsAbove = ( ( Math . floor ( ( maxSelectDropDownHeightAbove - verticalPadding ) / this . getHeight ( ) ) ) ) ;
462470
463471 // If we are only doing pre-layout check/adjust position only
464472 // Calculate vertical space available, flip up if insufficient
465- // Use reflected padding on parent select, ContextView style properties not available before DOM attachment
473+ // Use reflected padding on parent select, ContextView style
474+ // properties not available before DOM attachment
475+
466476 if ( preLayoutPosition ) {
477+ // Check if select moved out of viewport , do not open
478+ // If at least one option cannot be shown, don't open the drop-down or hide/remove if open
479+
480+ if ( ( selectPosition . top + selectPosition . height ) > ( window . innerHeight - 22 )
481+ || selectPosition . top < SelectBoxList . DEFAULT_DROPDOWN_MINIMUM_TOP_MARGIN
482+ || ( ( maxVisibleOptionsBelow < 1 ) && ( maxVisibleOptionsAbove < 1 ) ) ) {
483+ // Indicate we cannot open
484+ return false ;
485+ }
467486
487+ // Determine if we have to flip up
468488 // Always show complete list items - never more than Max available vertical height
469- if ( listHeight + verticalPadding > maxSelectDropDownHeight ) {
470- const maxVisibleOptions = ( ( Math . floor ( ( maxSelectDropDownHeight - verticalPadding ) / this . getHeight ( ) ) ) ) ;
471-
472- // Check if we can at least show min items otherwise flip above
473- if ( maxVisibleOptions < SelectBoxList . DEFAULT_MINIMUM_VISIBLE_OPTIONS ) {
474- this . _dropDownPosition = AnchorPosition . ABOVE ;
475- } else {
476- this . _dropDownPosition = AnchorPosition . BELOW ;
477- }
489+ if ( maxVisibleOptionsBelow < SelectBoxList . DEFAULT_MINIMUM_VISIBLE_OPTIONS
490+ && maxVisibleOptionsAbove > maxVisibleOptionsBelow
491+ && this . options . length > maxVisibleOptionsBelow
492+ ) {
493+ this . _dropDownPosition = AnchorPosition . ABOVE ;
494+ } else {
495+ this . _dropDownPosition = AnchorPosition . BELOW ;
478496 }
479497 // Do full layout on showSelectDropDown only
480- return ;
498+ return true ;
499+ }
500+
501+ // Check if select out of viewport or cutting into status bar
502+ if ( ( selectPosition . top + selectPosition . height ) > ( window . innerHeight - 22 )
503+ || selectPosition . top < SelectBoxList . DEFAULT_DROPDOWN_MINIMUM_TOP_MARGIN
504+ || ( this . _dropDownPosition === AnchorPosition . BELOW && maxVisibleOptionsBelow < 1 )
505+ || ( this . _dropDownPosition === AnchorPosition . ABOVE && maxVisibleOptionsAbove < 1 ) ) {
506+ // Cannot properly layout, close and hide
507+ this . hideSelectDropDown ( true ) ;
508+ return false ;
481509 }
482510
483511 // Make visible to enable measurements
@@ -486,15 +514,22 @@ export class SelectBoxList implements ISelectBoxDelegate, IVirtualDelegate<ISele
486514 // SetUp list dimensions and layout - account for container padding
487515 // Use position to check above or below available space
488516 if ( this . _dropDownPosition === AnchorPosition . BELOW ) {
517+ if ( this . _isVisible && maxVisibleOptionsBelow + maxVisibleOptionsAbove < 1 ) {
518+ // If drop-down is visible, must be doing a DOM re-layout, hide since we don't fit
519+ // Hide drop-down, hide contextview, focus on parent select
520+ this . hideSelectDropDown ( true ) ;
521+ return false ;
522+ }
523+
489524 // Set container height to max from select bottom to margin (default/minBottomMargin)
490- if ( listHeight + verticalPadding > maxSelectDropDownHeight ) {
491- listHeight = ( ( Math . floor ( ( maxSelectDropDownHeight - verticalPadding ) / this . getHeight ( ) ) ) * this . getHeight ( ) ) ;
525+ if ( minRequiredDropDownHeight > maxSelectDropDownHeightBelow ) {
526+ listHeight = ( maxVisibleOptionsBelow * this . getHeight ( ) + verticalPadding ) ;
492527 }
493528 } else {
494529 // Set container height to max from select top to margin (default/minTopMargin)
495- maxSelectDropDownHeight = ( selectPosition . top - SelectBoxList . DEFAULT_DROPDOWN_MINIMUM_TOP_MARGIN ) ;
496- if ( listHeight + verticalPadding > maxSelectDropDownHeight ) {
497- listHeight = ( ( Math . floor ( ( maxSelectDropDownHeight - SelectBoxList . DEFAULT_DROPDOWN_MINIMUM_TOP_MARGIN ) / this . getHeight ( ) ) ) * this . getHeight ( ) ) ;
530+ if ( minRequiredDropDownHeight > maxSelectDropDownHeightAbove ) {
531+ // listHeight = ((Math.floor((maxSelectDropDownHeightBelow - verticalPadding) / this.getHeight())) * this.getHeight());
532+ listHeight = ( maxVisibleOptionsAbove * this . getHeight ( ) + verticalPadding ) ;
498533 }
499534 }
500535
@@ -522,6 +557,9 @@ export class SelectBoxList implements ISelectBoxDelegate, IVirtualDelegate<ISele
522557 this . selectDropDownListContainer . setAttribute ( 'tabindex' , '0' ) ;
523558 dom . toggleClass ( this . selectElement , 'synthetic-focus' , true ) ;
524559 dom . toggleClass ( this . selectDropDownContainer , 'synthetic-focus' , true ) ;
560+ return true ;
561+ } else {
562+ return false ;
525563 }
526564 }
527565
0 commit comments