components icon indicating copy to clipboard operation
components copied to clipboard

feat(dialog): Ability to present MatDialog fullscreen

Open asido opened this issue 7 years ago • 18 comments
trafficstars

Bug, feature request, or proposal:

feature request

What is the expected behavior?

An ability to present modal dialogs fullscreen.

What is the current behavior?

Can't present modal dialogs fullscreen.

What are the steps to reproduce?

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

This feature is in Material design specifications: https://material.io/guidelines/components/dialogs.html#dialogs-full-screen-dialogs

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

Is there anything else we should know?

asido avatar Feb 22 '18 18:02 asido

You should be able to make a dialog fullscreen by setting its width and maxWidth to 100vw.

crisbeto avatar Feb 22 '18 19:02 crisbeto

Width works fine, but the height doesn't.

I forked the "What's your name?" dialog sample, added width and height overflowing content and set maxWidth/maxHeight to 100vw/100vh: See here

The maxHeight is hardcoded in the library to be 65vh. I believe it's due to mobile devices, where 100vh always overflows the visible document?

You can go ahead and set height:100vh, but that will simply stretch the frame, while the content will overflow at 65vh.

asido avatar Mar 16 '18 09:03 asido

That's a general issue with the dialog that'll go away with https://github.com/angular/material2/pull/9236. Until then you should be able to work around it by overriding it in your CSS.

crisbeto avatar Mar 16 '18 21:03 crisbeto

Would be nice to be able to configure full screen when width < 600 px (on mobile).

jtcrowson avatar May 18 '18 20:05 jtcrowson

I came across this issue while looking specifically for a way to open a dialog full screen only on small devices. This seems like something that gets asked a lot so I wrote a blog post about it and provided my solution there.

Essentially what we can do is resize the open dialog when the device size changes by using the BreakpointObserver class. You have to know the size you want to use on large screens, but other than that I haven't found any problems with it.

Hope this helps someone else.

@jtcrowson I think what I wrote up solves your problem and it's pretty easy to implement.

engineer-andrew avatar May 20 '18 00:05 engineer-andrew

@jtcrowson Here's my solution

First of all - as an aside - we need to stop using 100vh. Forget it even exists for the purposes of dialogs. It flat out doesn't work well on mobile devices with toolbars that hide once you start scrolling and can correspond to a height that's taller than the actual visible viewport. What's great about the overlay API is it sticks everything in a DIV anchored on top of the whole site, so using calc(100% - 2em) works very well.

What I'm doing is taking advantage of the fact that MatDialog allows you to add css classes for the overlay and panel. When you open a dialog you add these parameters to 'options':

    {
            backdropClass: 'logindialog-overlay',
            panelClass: 'logindialog-panel'
    }

Then the following css is used to change the size of the dialog when on mobile and make it fullscreen.

.logindialog-overlay
{
    .mobile &
    {
        background: black;
    }
}

.logindialog-panel
{
    .mobile &
    {
        // important is used because MatDialog applies css to the elements
        max-width: none !important;
        max-height: none !important;
    
        width: calc(100% - 2em) !important;
        height: calc(100% - 2em) !important;
    }
}

You can choose to just use media queries to enable this (and remove the .mobile class) - but this is Angular and we're smarter than that and we're probably using something like Anglar Flex Layout

So what is .mobile you are probably asking? Well it's a class that I am applying to the overlay container element - so as to give me a 'modernizr' style css parent class I can then 'consume'. I do this inside my LoginDialogComponent.


    constructor(
        public dialogRef: MatDialogRef<LoginDialogComponent>,
        private renderer: Renderer2,
        private smartLayout: SmartLayoutService,
        public overlayC: OverlayContainer,

        @Inject(MAT_DIALOG_DATA) public data: ILoginDialogData) {

            // reference to container element where the overlays get added to
            const overlay = overlayC.getContainerElement();

            smartLayout.getObservable('mobile').subscribe((isMobile) => {

                if (isMobile){
                    renderer.addClass(overlay, 'mobile');
                } else {
                    renderer.removeClass(overlay, 'mobile');
                }
            });
    }

SmartLayout is my extension to FlexLayout that uses MediaMonitor to monitor for changes to media queries.

simeyla avatar Jul 06 '18 04:07 simeyla

Here's my solution. What about a PopupService?

------------------ popup.service.ts ------------------

import { Injectable, TemplateRef } from '@angular/core';
import { Breakpoints, BreakpointObserver } from '@angular/cdk/layout';
import { MatDialogRef, MatDialog, MatDialogConfig } from '@angular/material';
import { ComponentType } from '@angular/cdk/portal';

@Injectable({
providedIn: 'root'
})

export class PopupService {

constructor(private dialog: MatDialog, private breakpointObserver: BreakpointObserver) { }

matDialogRef: any;
smallDialogSubscription: any;

open(componentOrTemplateRef: ComponentType | TemplateRef, mobileWidth: string, data?: MatDialogConfig): MatDialogRef {
if (data) {
data.maxWidth = '100vw';
data.maxHeight = '100vh';
}

this.matDialogRef = this.dialog.open(componentOrTemplateRef, data);

this.smallDialogSubscription = this.breakpointObserver.observe([Breakpoints.XSmall, Breakpoints.Small])
.subscribe(size => {
if (size.matches) {
this.matDialogRef.updateSize('100%', '100%');
} else {
this.matDialogRef.updateSize(mobileWidth, 'auto');
}
});

return this.matDialogRef;
}

close(): void {
this.smallDialogSubscription.unsubscribe();
this.matDialogRef.close();
}

}

--------- account.component.ts --------

...
editAccount() {
this.popupservice.open(AccountEditComponent, '800px', {
data: {}
});
}
...

--------- account-edit.component.ts --------

...
close() {
this.popupservice.close();
}
...

Based on this blog

nicoraffin avatar Dec 14 '18 10:12 nicoraffin

You can do it in pure CSS .cdk-overlay-pane { width: 90%; height: 90%; } @media(max-width: 1000px) { .cdk-overlay-pane { width: 100%; height: 100%; } }

and just don't put the width and height when instantiating your modal (you can pass max width and height) const dialogRef = this.dialog.open(AvailableRoomsComponent, { maxWidth: '100vw', maxHeight: '100vh' });

Tibo46 avatar Jan 02 '19 11:01 Tibo46

I got it working with this in css:

.cdk-overlay-pane {
  max-width: 95vw !important;
}

But then I still have to add size to my dialog creation to take advantage of that:

let dialogRef = this.dialog.open(MyDialogComponent, {width: '100vw', data: {blah}});

aehven avatar Feb 06 '19 23:02 aehven

It works using:

const dialogRef = this.dialog.open(AvailableRoomsComponent, {
  minWidth: '100vw',
  height: '100vh'
});

makamekm avatar May 28 '19 09:05 makamekm

@makamekm Awesome. You saved my day! This technique works perfectly.

kjdeepak avatar Jun 27 '19 10:06 kjdeepak

just wanted to drop my workaround-hack, in case the above doesn't work for you, like me, i used the mat-content tag to keep my buttons in view

  .cdk-overlay-pane {
    max-width: none !important;
    flex: 1 1 100%;
    height: 100%;
  }
  .mat-dialog-content {
    max-height: none !important;
    flex: auto;
  }

wutzebaer avatar Feb 03 '20 12:02 wutzebaer

It's a good workaround but it's not really a full-screen dialog as specified by Material Design - (find "Full-screen dialog") i.E. the closing "X" on the top left and "save" button on the top right.

phrei avatar Apr 08 '20 13:04 phrei

The workarounds only take care of the size, the action buttons layout is different in the material guidelines. They are at the top.

Screen Shot 2020-05-05 at 2 29 31 PM

Wildhammer avatar May 05 '20 18:05 Wildhammer

For my project, I did this feature module to manage dynamic dialog layout.

The module has four functions:

  • openCenterFull: Open dialog with desktop (vertically and horizontally centered), and mobile (full width and height)
  • openCenterBottom: Open dialog with desktop (vertically and horizontally centered), and mobile (bottom, like bottomSheet )
  • openRightFull: Open dialog with desktop (right with full height ), and mobile (fullscreen)
  • openLeftFull: Open dialog with desktop (left and full height), and mobile (fullscreen)

If you only need to present the dialog in fullscreen mode, you can search in the first two functions.

https://gist.github.com/faan11/7eb04a23e6d63d1a0a95260c8211dec5

faan11 avatar Sep 06 '20 09:09 faan11

As the dialog component in Angualr Material Experimantal has adopted the implementation from MDC, where full-screen dialog is available, I believe it is super easy for the team to implement this feature, but they didn't 😕.

Char2sGu avatar Apr 18 '22 18:04 Char2sGu

I don't understand how this wasn't implemented from the beginning. Who wants to look at a tiny box inside a tiny box, which is their mobile phone screen? Doesn't it make sense to place dialog buttons down the bottom centre in a vertical row for people to easily use their thumb, instead of stabbing around the middle of the screen?

For all of the effort poured into Material Design these kinds of considerations seem so basic, and I still have to twist the dialog's styling and button placement to achieve some basic usability standards.

jpike88 avatar Dec 19 '22 12:12 jpike88

Is there any progress in this? I would really enjoy this feature too, so the app would be much more mobile friendly :)

lgicc avatar Aug 08 '24 16:08 lgicc

Hi everyone, I would like to point out that even the most recent Material Design specifications (v3) include the close button and the action button in the title bar.

Image

KinokoF avatar Apr 07 '25 15:04 KinokoF

I'm sharing my workaround while waiting for a better solution.

Image

app.component.html

<button mat-raised-button (click)="openDialog()">Open dialog</button>

app.component.ts

@Component({
  selector: 'app-root',
  imports: [MatButtonModule],
  templateUrl: './app.component.html',
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AppComponent {
  readonly dialog = inject(MatDialog);

  openDialog(): void {
    this.dialog.open(FullScreenDialogComponent, {
      width: '100vw',
      minWidth: '100vw',
      maxWidth: '100vw',
      height: '100vh',
      panelClass: 'full-screen-dialog',
    });
  }
}

full-screen-dialog.component.html

<div class="mat-dialog-toolbar">
  <button mat-icon-button class="mat-dialog-toolbar-close" mat-dialog-close>
    <mat-icon>close</mat-icon>
  </button>
  <h2 class="mat-dialog-toolbar-title">Full-screen dialog title</h2>
  <div style="flex: 1"></div>
  <button mat-button (click)="save()">Save</button>
</div>
<mat-dialog-content>
  <p>Lorem ipsum dolor sit amet...</p>
</mat-dialog-content>

full-screen-dialog.component.html

@Component({
  selector: 'app-full-screen-dialog',
  imports: [MatDialogModule, MatButtonModule, MatIconModule],
  templateUrl: './full-screen-dialog.component.html',
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class FullScreenDialogComponent {
  save(): void {}
}

styles.scss

@use '@angular/material' as mat;

[...]

.full-screen-dialog {
  @include mat.dialog-overrides((
    container-shape: none,
  ));

  .mat-dialog-toolbar {
    display: flex;
    align-items: center;
    height: 56px;
    padding: 0 16px;

    .mat-dialog-toolbar-close {
      margin-right: 16px;
    }

    .mat-dialog-toolbar-title {
      margin: 0;

      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;

      color: var(--mat-sys-on-surface);
      font-family: var(--mat-sys-headline-small-font);
      line-height: var(--mat-sys-headline-small-line-height);
      font-size: var(--mat-sys-headline-small-size);
      font-weight: var(--mat-sys-headline-small-weight);
      letter-spacing: var(--mat-sys-headline-small-tracking);
    }
  }

  .mat-mdc-dialog-content {
    max-height: 100vh;
  }
}

KinokoF avatar Apr 07 '25 16:04 KinokoF

As the comment above suggests, it's currently quite verbose to implement full-screen mode.

I think it would be useful to have a new property that acts as a shortcut.

For example:

// Warning: mock code!
this.dialog.open(FullScreenDialogComponent, { fullScreen: true });

Instead of:

this.dialog.open(FullScreenDialogComponent, {
  width: '100vw',
  minWidth: '100vw',
  maxWidth: '100vw',
  height: '100vh',
  panelClass: 'full-screen-dialog',
});

Setting the fullScreen property to true would cause the width, minWidth, maxWidth, height, minHeight, maxHeight properties to stop having any effect. In addition to turning off round borders.

It would also be useful to have built-in directives and components to manage the positioning and styles of the toolbar, close button, toolbar title and action button.

KinokoF avatar Apr 08 '25 17:04 KinokoF