blob: 04d5923e80e1f8c91f8a53c62a00cf844ae70d04 [file] [log] [blame]
/**
* @license
* Copyright 2016 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import '../gr-button/gr-button';
import {customElement, property, query} from 'lit/decorators.js';
import {GrButton} from '../gr-button/gr-button';
import {css, html, LitElement, PropertyValues} from 'lit';
import {sharedStyles} from '../../../styles/shared-styles';
import {fontStyles} from '../../../styles/gr-font-styles';
import {when} from 'lit/directives/when.js';
declare global {
interface HTMLElementTagNameMap {
'gr-dialog': GrDialog;
}
}
@customElement('gr-dialog')
export class GrDialog extends LitElement {
/**
* Fired when the confirm button is pressed.
*
* @event confirm
*/
/**
* Fired when the cancel button is pressed.
*
* @event cancel
*/
@query('#cancel')
cancelButton?: GrButton;
@query('#confirm')
confirmButton?: GrButton;
@property({type: String, attribute: 'confirm-label'})
confirmLabel = 'Confirm';
// Supplying an empty cancel label will hide the button completely.
@property({type: String, attribute: 'cancel-label'})
cancelLabel = 'Cancel';
@property({type: Boolean, attribute: 'loading'})
loading = false;
@property({type: String, attribute: 'loading-label'})
loadingLabel = 'Loading...';
// TODO: Add consistent naming after Lit conversion of the codebase
@property({type: Boolean})
disabled = false;
@property({type: Boolean})
disableCancel = false;
@property({type: Boolean, attribute: 'confirm-on-enter'})
confirmOnEnter = false;
@property({type: String, attribute: 'confirm-tooltip'})
confirmTooltip?: string;
override firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
if (!this.getAttribute('role')) this.setAttribute('role', 'dialog');
}
static override get styles() {
return [
sharedStyles,
fontStyles,
css`
:host {
color: var(--primary-text-color);
display: block;
max-height: 90vh;
overflow: auto;
}
.container {
display: flex;
flex-direction: column;
max-height: 90vh;
padding: var(--spacing-xl);
}
header {
flex-shrink: 0;
padding-bottom: var(--spacing-xl);
}
main {
display: flex;
flex-shrink: 1;
width: 100%;
flex: 1;
/* IMPORTANT: required for firefox */
min-height: 0px;
}
main .overflow-container {
flex: 1;
overflow: auto;
}
footer {
display: flex;
flex-shrink: 0;
padding-top: var(--spacing-xl);
align-items: center;
}
.flex-space {
flex-grow: 1;
}
gr-button {
margin-left: var(--spacing-l);
}
.hidden {
display: none;
}
.loadingSpin {
width: 18px;
height: 18px;
}
`,
];
}
override render() {
// Note that we are using (e: Event) => this._handleKeyDown because the
// tests mock out _handleKeydown so the lookup needs to be dynamic, not
// bound statically here.
return html`
<div
class="container"
@keydown=${(e: KeyboardEvent) => this._handleKeydown(e)}
>
<header class="heading-3"><slot name="header"></slot></header>
<main>
<div class="overflow-container">
<slot name="main"></slot>
</div>
</main>
<footer>
${when(
this.loading,
() => html`
<span
class="loadingSpin"
role="progressbar"
aria-label=${this.loadingLabel}
></span>
<span class="loadingLabel"> ${this.loadingLabel} </span>
`
)}
<div class="flex-space"></div>
<gr-button
id="cancel"
class=${this.cancelLabel.length ? '' : 'hidden'}
link
?disabled=${this.disableCancel}
@click=${(e: Event) => this.handleCancelTap(e)}
>
${this.cancelLabel}
</gr-button>
<gr-button
id="confirm"
link
primary
@click=${this._handleConfirm}
?disabled=${this.disabled}
title=${this.confirmTooltip ?? ''}
>
${this.confirmLabel}
</gr-button>
</footer>
</div>
`;
}
override updated(changedProperties: PropertyValues) {
if (changedProperties.has('confirmTooltip')) {
this.updateTooltip();
}
}
private updateTooltip() {
const confirmButton = this.confirmButton;
if (!confirmButton) return;
if (this.confirmTooltip) {
confirmButton.setAttribute('has-tooltip', 'true');
} else {
confirmButton.removeAttribute('has-tooltip');
}
}
_handleConfirm(e: Event) {
if (this.disabled) {
return;
}
e.preventDefault();
e.stopPropagation();
this.dispatchEvent(
new CustomEvent('confirm', {
composed: true,
bubbles: false,
})
);
}
private handleCancelTap(e: Event) {
e.preventDefault();
e.stopPropagation();
this.dispatchEvent(
new CustomEvent('cancel', {
composed: true,
bubbles: false,
})
);
}
_handleKeydown(e: KeyboardEvent) {
if (this.confirmOnEnter && e.key === 'Enter') {
this._handleConfirm(e);
}
}
resetFocus() {
if (this.disabled && this.cancelLabel) {
this.cancelButton!.focus();
} else {
this.confirmButton!.focus();
}
}
}