diff --git a/demo/components/expressive-component.js b/demo/components/expressive-component.js index ec551d3..e9120ff 100644 --- a/demo/components/expressive-component.js +++ b/demo/components/expressive-component.js @@ -693,10 +693,21 @@ class ExpressiveComponent extends LitElement { -
Dialog title
+
Dialog Title
A simple dialog with free-form content.
- this.renderRoot.querySelector('#dialog1').close()}> + this.renderRoot.querySelector('#dialog1').close('cancel')}> + Cancel + + this.renderRoot.querySelector('#dialog1').close('ok')}> Ok
diff --git a/demo/dialog-demo.html b/demo/dialog-demo.html new file mode 100644 index 0000000..1ac9f83 --- /dev/null +++ b/demo/dialog-demo.html @@ -0,0 +1,260 @@ + + + + + + Material 3 Dialog Demo + + + + + + + + + + + + + + + +
+

Material 3 Dialogs

+ Back to Components +
+ +

+ Dialogs provide critical information and decisions in a user flow, featuring Level 3 elevation, standard 28dp + shape tokens, and smooth M3 entry/exit motion. +

+ +
+

1. Basic & Confirmation Dialog

+

Standard modal dialog with headline, body, and action buttons.

+
+ Open Basic Dialog +
+ + +
Delete item?
+
+ Are you sure you want to delete this file? This action is permanent and cannot be undone. +
+
+ Cancel + Delete +
+
+
+ +
+

2. Alert Dialog (`type="alert"`)

+

+ Alert dialogs interrupt the user with urgent information. They have role="alertdialog" and cannot + be dismissed by tapping the backdrop scrim. +

+
+ Open Alert Dialog +
+ + + warning +
Discard unsaved draft?
+
+ You have pending changes that will be lost forever if you discard them now. Tapping outside the dialog will + not dismiss it. +
+
+ Keep Editing + Discard +
+
+
+ +
+

3. Stacked Actions (`stacked`)

+

+ For long action button labels, buttons stack vertically with the primary confirming action above the dismissive + action. +

+
+ Open Stacked Dialog +
+ + +
Turn on backup?
+
Photos and videos will be saved automatically to your Google Photos cloud library.
+
+ Don't Back Up + Turn On Cloud Backup +
+
+
+ +
+

4. Scrollable Dialog with Dividers

+

+ When content overflows, vertical scrolling is activated, and 1dp dividers dynamically appear beneath the + headline and above the actions. +

+
+ Open Scrollable Dialog +
+ + +
Terms and Conditions
+
+

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed ut perspiciatis unde omnis iste natus error sit + voluptatem accusantium doloremque laudantium. +

+

+ Totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt + explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit. +

+

+ Sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui + dolorem ipsum quia dolor sit amet, consectetur, adipisci velit. +

+

+ Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex + ea commodi consequatur? +

+
+
+ Decline + Accept +
+
+
+ +
+

5. Full-Screen Dialog (`fullscreen`)

+

Full-screen dialogs fill the entire viewport for complex workflows and form tasks.

+
+ Open Full-Screen Dialog +
+ + +
Create Event
+
+

This dialog spans the full viewport (100% width and height, 0dp corner radius, 0 elevation).

+

Fill out the details below before saving your new calendar event.

+
+
+ Discard + Save +
+
+
+ +
Last Action: None
+ + + + diff --git a/demo/index.html b/demo/index.html index c1bb176..bcd128e 100644 --- a/demo/index.html +++ b/demo/index.html @@ -263,7 +263,8 @@ Bottom Sheet Demo • App Bar Demo • List Demo • Date Picker Demo • - Time Picker Demo + Time Picker Demo • + Dialog Demo diff --git a/dialog/README.md b/dialog/README.md index cbe86c6..049ecc1 100644 --- a/dialog/README.md +++ b/dialog/README.md @@ -1,40 +1,223 @@ # Dialog +Dialogs provide important prompts in a user flow. They can require an action, communicate information, or help users accomplish a task. + +`` is fully compliant with the [Material Design 3 Dialogs Specification](https://m3.material.io/components/dialogs/overview), supporting Level 3 elevation, standard 28dp rounded containers, M3 motion curves, scroll-dependent dividers, stacked actions, and alert/modal workflows. + +## Import + +```javascript +import 'material/dialog/dialog.js' +// or import the Dialog class: +import { Dialog } from 'material/dialog/dialog.js' +``` + +--- + ## Usage -First, create the dialog. This will be hidden by default, unless you specify the `open` attribute on the `md-dialog` tag. +### Basic Confirmation Dialog ```html - -
- Delete item + +
Delete item?
+
This will permanently remove the selected file. This action cannot be undone.
+
+ Cancel + Delete
-
- Are you sure you want to delete this item? + + +Open Dialog +``` + +### Programmatic Control + +```javascript +const dialog = document.querySelector('md-dialog') + +// Open the dialog (returns Promise resolving when open animation finishes) +await dialog.show() + +// Close the dialog with an optional return value +await dialog.close('confirm') +console.log(dialog.returnValue) // 'confirm' +``` + +--- + +## Variants & Features + +### 1. Alert Dialog (`type="alert"`) + +Alert dialogs interrupt users with urgent, critical information. Following M3 guidelines, alert dialogs render with `role="alertdialog"` and **cannot be dismissed by tapping outside on the scrim**; the user must make an explicit choice. + +```html + + warning +
Discard unsaved changes?
+
You have unsaved changes in your document. Leaving now will cause them to be lost.
+
+ Keep Editing + Discard
+
+``` + +> **Tip:** You can also use `prevent-scrim-close` on standard dialogs to prevent backdrop clicks from dismissing the dialog without changing its ARIA role to `alertdialog`. + +### 2. Dialog with Header Icon + +When an icon is placed in the `icon` slot, the dialog automatically centers the icon and title according to the M3 specification. + +```html + + delete +
Permanently delete?
+
All associated data will be erased from our servers immediately.
- this.renderRoot.querySelector("#confirm-remove").close()}>Cancel - Ok + Cancel + Delete
+``` + +### 3. Stacked Actions (`stacked`) + +When action buttons have longer labels that cannot comfortably fit side-by-side, add the `stacked` attribute. Following Material 3 guidelines, stacked actions arrange buttons vertically, placing the **confirming action on top** and the **dismissive action below**. -Delete +```html + +
Turn on backup?
+
Photos and videos will be saved automatically to your cloud storage account.
+
+ Don't Back Up + Turn On Cloud Backup +
+
``` -Then if someone click the button above, it will call this: +### 4. Full-Screen Dialog (`fullscreen`) -```js -openDialog() { - this.renderRoot.querySelector("#confirm-remove").show() -} +For complex tasks on compact or mobile viewports, the `fullscreen` attribute scales the dialog to fill 100% of the screen with 0dp corner radius and Level 0 elevation. + +```html + +
Create Event
+
+
+ +
+
+
+ Discard + Save +
+
``` -If you need to call your API or do some action based on what the user chooses, you'd do something like this: +### 5. Scrollable Content & Dynamic Dividers + +When content overflows the available height, `` automatically scrolls vertically and displays 1dp dividers: -```js -deleteConfirmed() { - // Call your API to delete the item here +- A divider beneath the headline when content is scrolled down past the top. +- A divider above the actions when content extends beneath the viewport bottom. + +### 6. HTML Form Integration (`method="dialog"`) + +Buttons with a `value` attribute inside a `
` automatically close the dialog and set `returnValue`: + +```html + +
Reset settings?
+ +

All customized preferences will be restored to their initial defaults.

+ +
+ Cancel + Reset +
+
- this.renderRoot.querySelector("#confirm-remove").close() -} + ``` + +--- + +## API Reference + +### Properties & Attributes + +| Property | Attribute | Type | Default | Description | +| :------------------ | :-------------------- | :-------- | :------ | :------------------------------------------------------------------------------------ | +| `open` | `open` | `boolean` | `false` | Opens or closes the dialog. Reflects to attribute. | +| `quick` | `quick` | `boolean` | `false` | Skips opening and closing animations when `true`. | +| `returnValue` | `return-value` | `string` | `''` | Gets or sets the return value for the dialog. | +| `type` | `type` | `string` | `''` | Set to `'alert'` to enable `role="alertdialog"` and prevent dismissal on scrim click. | +| `stacked` | `stacked` | `boolean` | `false` | Stacks action buttons vertically with confirming action on top. | +| `preventScrimClose` | `prevent-scrim-close` | `boolean` | `false` | Prevents clicking the backdrop scrim from closing the dialog. | +| `fullscreen` | `fullscreen` | `boolean` | `false` | Displays the dialog in full-screen mode (100% viewport, 0 radius). | + +### Methods + +| Method | Parameters | Return Type | Description | +| :-------- | :--------------------- | :-------------- | :------------------------------------------------------------------------------ | +| `show()` | none | `Promise` | Opens the dialog and resolves after the entry animation finishes. | +| `close()` | `returnValue?: string` | `Promise` | Closes the dialog with optional return value and resolves after exit animation. | + +### Events + +| Event | Bubble | Composed | Description | +| :------- | :----: | :------: | :-------------------------------------------------------------------- | +| `open` | Yes | No | Dispatched before opening animations begin. Cancelable. | +| `opened` | Yes | No | Dispatched after the dialog has fully opened and animations complete. | +| `close` | Yes | No | Dispatched before closing animations begin. Cancelable. | +| `closed` | Yes | No | Dispatched after the dialog has fully closed and animations complete. | +| `cancel` | Yes | No | Dispatched when canceled via Escape key or scrim click. Cancelable. | + +### Slots + +| Name | Description | +| :--------- | :--------------------------------------------------------------------------------- | +| `headline` | The title / headline of the dialog (uses M3 Headline Small typography). | +| `content` | The body content (uses M3 Body Medium typography and supports vertical scrolling). | +| `actions` | Action buttons (typically `` in text, filled, or tonal styles). | +| `icon` | Optional header icon displayed above the headline. | + +--- + +## Design Tokens (CSS Custom Properties) + +| Token | Default Value | Description | +| :---------------------------------------- | :---------------------------------------------------- | :------------------------------------------------------- | +| `--md-dialog-container-color` | `var(--md-sys-color-surface-container-high, #ece6f0)` | Background color of the container. | +| `--md-dialog-container-elevation` | `3` | M3 elevation level (Level 3 for standard modal dialogs). | +| `--md-dialog-container-shadow-color` | `var(--md-sys-color-shadow, #000)` | Shadow color for elevation. | +| `--md-dialog-container-shape` | `var(--md-sys-shape-corner-extra-large, 28px)` | Corner radius of the container. | +| `--md-dialog-container-max-width` | `min(560px, calc(100% - 48px))` | Maximum container width. | +| `--md-dialog-container-max-height` | `min(560px, calc(100% - 48px))` | Maximum container height. | +| `--md-dialog-container-min-width` | `280px` | Minimum container width. | +| `--md-dialog-container-min-height` | `140px` | Minimum container height. | +| `--md-dialog-headline-color` | `var(--md-sys-color-on-surface, #1d1b20)` | Text color of the headline. | +| `--md-dialog-headline-font` | `var(--md-sys-typescale-headline-small-font)` | Font family of the headline. | +| `--md-dialog-headline-size` | `1.5rem` (`24px`) | Font size of the headline. | +| `--md-dialog-headline-line-height` | `2rem` (`32px`) | Line height of the headline. | +| `--md-dialog-headline-weight` | `400` | Font weight of the headline. | +| `--md-dialog-supporting-text-color` | `var(--md-sys-color-on-surface-variant, #49454f)` | Text color of the content body. | +| `--md-dialog-supporting-text-font` | `var(--md-sys-typescale-body-medium-font)` | Font family of the content body. | +| `--md-dialog-supporting-text-size` | `0.875rem` (`14px`) | Font size of the content body. | +| `--md-dialog-supporting-text-line-height` | `1.25rem` (`20px`) | Line height of the content body. | +| `--md-dialog-supporting-text-weight` | `400` | Font weight of the content body. | +| `--md-dialog-icon-color` | `var(--md-sys-color-secondary, #625b71)` | Icon color in header. | +| `--md-dialog-icon-size` | `24px` | Icon dimensions. | +| `--md-dialog-scrim-color` | `var(--md-sys-color-scrim, #000)` | Background color of backdrop scrim. | +| `--md-dialog-scrim-opacity` | `32%` | Opacity of backdrop scrim. | diff --git a/dialog/dialog.js b/dialog/dialog.js index 24e3ca8..ed122ad 100644 --- a/dialog/dialog.js +++ b/dialog/dialog.js @@ -4,6 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ import '../divider/divider.js' +import '../internal/elevation/elevation.js' import { html, isServer, LitElement, nothing, css } from 'lit' import { classMap } from 'lit/directives/class-map.js' import { requestUpdateOnAriaChange } from '../internal/aria/delegate.js' @@ -25,6 +26,14 @@ export class Dialog extends LitElement { quick: { type: Boolean, reflect: true }, returnValue: { type: String }, type: { type: String }, + stacked: { type: Boolean, reflect: true }, + preventScrimClose: { type: Boolean, reflect: true, attribute: 'prevent-scrim-close' }, + fullscreen: { type: Boolean, reflect: true }, + isAtScrollTop: { state: true }, + isAtScrollBottom: { state: true }, + hasHeadline: { state: true }, + hasActions: { state: true }, + hasIcon: { state: true }, } /** @@ -59,6 +68,10 @@ export class Dialog extends LitElement { * https://developer.mozilla.org/en-US/docs/Web/API/HTMLDialogElement/returnValue */ this.returnValue = '' + this.type = '' + this.stacked = false + this.preventScrimClose = false + this.fullscreen = false /** * Gets the opening animation for a dialog. Set to a new function to customize * the animation. @@ -211,6 +224,8 @@ export class Dialog extends LitElement { scrollable: scrollable, 'show-top-divider': scrollable && !this.isAtScrollTop, 'show-bottom-divider': scrollable && !this.isAtScrollBottom, + stacked: this.stacked, + fullscreen: this.fullscreen, } const { ariaLabel } = this return html` @@ -225,6 +240,7 @@ export class Dialog extends LitElement { @close=${this.handleClose} @keydown=${this.handleKeydown} .returnValue=${this.returnValue || nothing}> +