Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support positioning mat-select panel under the trigger #14105

Open
takamori opened this issue Nov 12, 2018 · 18 comments
Open

Support positioning mat-select panel under the trigger #14105

takamori opened this issue Nov 12, 2018 · 18 comments
Labels
area: material/select feature This issue represents a new feature or feature request rather than a bug or bug fix G This is is related to a Google internal issue P3 An issue that is relevant to core functions, but does not impede progress. Important, but not urgent

Comments

@takamori
Copy link

Bug, feature request, or proposal:

disableOptionCentering as discussed on https://material.angular.io/components/select/api does not appear to have any effect and prevents implementing the "Exposed Dropdown Menu" mentioned at https://material.io/design/components/menus.html#exposed-dropdown-menu .

What is the expected behavior?

Setting disableOptionCentering to true would change the options to appear either below or above the mat-select field.

What is the current behavior?

The options appear over the field, whether or not disableOptionCentering is used.

What are the steps to reproduce?

https://stackblitz.com/edit/angular-khatke
I've tried using


<mat-select placeholder="Favorite food" [disableOptionCentering]="true">

What is the use-case or motivation for changing an existing behavior?

This prevents the ability to implement the Exposed Dropdown Menu.

Which versions of Angular, Material, OS, TypeScript, browsers are affected?

Repro's at least while using Angular/Material npm 7.0.3 on Chrome 70.0.3538.77 on Mac.

Is there anything else we should know?

@crisbeto
Copy link
Member

It does work. You can see the difference in your example if you remove it, select the second option in the select and open it again.

@takamori
Copy link
Author

ah.... I didn't notice (or expect) that behavior. That's not quite the same as what I was looking to do, then, since the top choice overlaps the input field, which isn't the way that Exposed Dropdown Menu is spec'd. Is there an appropriate way to implement the Exposed Dropdown Menu from the Material Design spec?

@jelbourn jelbourn added feature This issue represents a new feature or feature request rather than a bug or bug fix P3 An issue that is relevant to core functions, but does not impede progress. Important, but not urgent G This is is related to a Google internal issue labels Nov 12, 2018
@jelbourn jelbourn changed the title [mat-select] disableOptionCentering does not seem to work. Support positioning mat-select panel under the trigger Nov 12, 2018
@jelbourn
Copy link
Member

It looks like the "exposed dropdown" is a new addition to the spec. @crisbeto we should think about adding an API to support that. It might make sense to deprecate disableOptionCentering and instead have something like a panelPosition property

@TheCom3di4n
Copy link

@jelbourn I know that the functionality is a bit like a select, but wouldn't it be nicer to add it to the menu component or even ship it as a standalone component, since there are one or two differences, e.g. the style? This would also help people who read or follow the Material Design Guidelines and then look for the corresponding component in the Angular Material library.

In addition, the exposed dropdown menu also contains also things like a "user input" mode or a "inside a toolbar" mode (...I'm not even sure if you can/should use it outside of such a "toolbar"...), see the "Behaviour" section in the MDG. https://material.io/design/components/menus.html#exposed-dropdown-menu

@takamori
Copy link
Author

@TheCom3di4n In my case, I was looking for the select functionality in the Material Design Guidelines, originally starting from "Text fields" and found it under menu, so I'd actually prefer that the Material Design Guideline folks consider at least providing a cross-link in the other direction.

@TheCom3di4n
Copy link

@takamori I fully agree with you, I also think that this is more of a select, as I already wrote above, and would feel more comfortable if they would place this component under the select in the Material Design Guidelines, since its behavior rather describes a kind of the native HTML select (...exactly one option can be selected...).

By the way, I find it really unfortunate, for reasons like these, that there is no issue tracker or public forum for the Material Design Guidelines in which you can ask questions or even start and have such discussions. But I guess that's intentional.

@tahaabu
Copy link

tahaabu commented Aug 26, 2019

The workaround for getting the exposed dropdown might just be adding margin in the panelClass.

here's the example: https://stackblitz.com/edit/angular-khatke-8xhpbc?file=styles.css

@kwong-yw
Copy link

@tahaabu that visually looks correct, but once the dropdown is activated, you can't click on the menu to close the dropdown– it looks like there's a .cdk-overlay-pane masking most of the menu. Did you ever find a way to get around this?

@Nikolay-Uvarov
Copy link

Nikolay-Uvarov commented Oct 25, 2019

@tahaabu We have faced with the same issue as @kwong-yw .
Can't close dropdown by the arrow or by the area above and to the left in our case.
CSS: margin: 32px 16px;
We can't shift .cdk-overlay-pane because we have another mat-select on the same page.

mat-select-issue

Fixed(not the final version):

HTML:
<mat-select #mySelect panelClass="server-group-drop-down-list" (openedChange)="openedChange($event)"
TS:
@ViewChild('mySelect') mySelect; ... public openedChange(opened: boolean): void { this.isOpened = opened; } @HostListener("document:click", ["$event.target"]) public onClick(targetElement) { if (this.isOpened) { const element = document.querySelector('.server-group-drop-down-list'); // your mat-select class if (!element.contains(targetElement)) { this.mySelect.close() } } }

I think you can close the dropdown on document click as well.

@akmjenkins
Copy link

The real way to fix this, without hacks, is to use the overlayDir property of the select and set it's positions.

https://material.angular.io/cdk/overlay/api

I do this AfterViewInit like this. There may be a better way:

export class SelectComponent implements AfterViewInit {
  @ViewChild(MatSelect) select: MatSelect;

  ngAfterViewInit() {
    this.select.overlayDir.positions = [
      {
        originX: 'center',
        originY: 'bottom',
        overlayX: 'center',
        overlayY: 'top'
      }
    ];
  }

@petrskalicka
Copy link

@akmjenkins Good point, but this option is already deprecated. It becomes private in version 10.

@isbor
Copy link

isbor commented Feb 27, 2020

Workaround which unfortunately require some code. Also you should properly handle disabled selects.

https://stackblitz.com/edit/angular-khatke-rtiahe

@jnenning
Copy link

jnenning commented Apr 9, 2020

Is there any alternative for solving this problem because I have a use case where the option should stick to the bottom or the top?

@eLarocque
Copy link

eLarocque commented Jul 17, 2020

I think that the underlying issue here is that everything happens in the overlay-pane element but it isn't accessible through the components that use it. For example, the mat-select has a panelClass Input which helps to customize the style of the panel, but in many cases it is not sufficient, especially if we need to "scope" the custom css in order to ensure it doesn't affect other components that use the cdk-overlay. What would be ideal is to also have access the cdk-overlay-pane that wraps the panel.

Maybe it makes sense to not only allow a "scope" to be added to the specific DOM panel elements of these components, but also the overlay-pane in which it resides. This is even easier to accomplish since there already exists such an Input in the overlay-directive : cdkConnectedOverlayPanelClass. If the mat-select (and possibly other such components) exposed this input through one of it's own, it would allow the customization of the overlay behaviors and styling.

I don't know if this goes against some internal architectural guidelines for components in general at Google, but if it is acceptable, maybe all components that make use of the overlay-directive could benefit from exposing the cdkConnectedOverlayPanelClass Input. In most cases the angular team would only need to support the class gets properly added to the overlay, and the responsibility of the custom behaviors and styling would fall in the hands of the developer adding the class. Might be a win-win for all.

@eLarocque
Copy link

Ended up using an approach similar to @isbor but with the twist of providing a custom implementation of the Overlay in the component's viewProviders. The new class extends the Overlay but adds logic to add a custom class in order to scope the overlay. The benefit to this is the classes are already set when the overlay renders, removing any kind of "temporary invalid states" that might be caused when attempting to manipulate the classes after the component renders.

See the example here:
https://stackblitz.com/edit/angular-issue-14105

@chandrakanthg
Copy link

interesting post. Could you help me to solve my current problem with select-panel position.
Issue: mat-options panel is overlapping the dropdown when the panel is not having enough space to display at the bottom of the dropdown hence I want to show it on the top of the dropdown without overlapping if we don't have enough space below.

the expected behaviour is: options cdk-overlay should display on top / below of the dropdown based on the space availability in the screen.

@chandrakanthg
Copy link

ngAfterViewInit() {
this.select.overlayDir.positions = [
{
originX: 'start',
originY: 'top',
overlayX: 'start',
overlayY: 'top'
},
{
originX: 'start',
originY: 'top',
overlayX: 'start',
overlayY: 'bottom'
}
];
}

this code snippet solves the above problem, we can change the positions my modifying the above option in the array.

@mtschneiders
Copy link

Ended up using an approach similar to @isbor but with the twist of providing a custom implementation of the Overlay in the component's viewProviders. The new class extends the Overlay but adds logic to add a custom class in order to scope the overlay. The benefit to this is the classes are already set when the overlay renders, removing any kind of "temporary invalid states" that might be caused when attempting to manipulate the classes after the component renders.

See the example here: https://stackblitz.com/edit/angular-issue-14105

On angular 11 and above, you can set the select overlay panel class via provider:

@Component({
  selector: "app-custom-select",
  templateUrl: "./custom-select.component.html",
  styleUrls: ["./custom-select.component.scss"],
  providers: [
    {
      provide: MAT_SELECT_CONFIG,
      useValue: { overlayPanelClass: 'custom-overlay-panel' },
    },
  ],
})

Example: Stackblitz

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: material/select feature This issue represents a new feature or feature request rather than a bug or bug fix G This is is related to a Google internal issue P3 An issue that is relevant to core functions, but does not impede progress. Important, but not urgent
Projects
None yet
Development

No branches or pull requests