padloc/packages/app/src/elements/alert-dialog.ts

175 lines
5.2 KiB
TypeScript

import { translate as $l } from "@padloc/locale/src/translate";
import { html, css, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators.js";
import { Dialog } from "./dialog";
import "./button";
const defaultButtonLabel = $l("OK");
export type AlertType = "info" | "warning" | "destructive" | "choice" | "question" | "success";
export interface AlertOptions {
message?: string | TemplateResult;
title?: string;
options?: (string | TemplateResult)[];
type?: AlertType;
icon?: string | null;
preventDismiss?: boolean;
vertical?: boolean;
preventAutoClose?: boolean;
maxWidth?: string;
width?: string;
hideOnDocumentVisibilityChange?: boolean;
}
@customElement("pl-alert-dialog")
export class AlertDialog extends Dialog<AlertOptions, number> {
@property()
buttonLabel: string = defaultButtonLabel;
@property()
dialogTitle: string = "";
@property()
message: string | TemplateResult = "";
@property({ reflect: true, attribute: "type" })
type: AlertType = "info";
@property()
icon: string | null = null;
@property({ attribute: false })
options: (string | TemplateResult)[] = [];
@property({ type: Boolean, reflect: true })
vertical: boolean = false;
@property()
maxWidth?: string;
@property()
width?: string;
static styles = [
...Dialog.styles,
css`
:host {
--pl-dialog-max-width: 20em;
}
:host([hide-icon]) .info-icon {
display: none;
}
:host([hide-icon]) .info-text,
:host([hide-icon]) .info-title {
text-align: center;
}
.info-text:not(.small) {
font-size: var(--font-size-default);
}
`,
];
renderContent() {
const { message, dialogTitle, options, icon, vertical } = this;
return html`
<div class="scrolling-vertically fit">
<div class="padded">
${dialogTitle || message
? html`
<div class="margined horizontal layout">
${icon ? html` <pl-icon class="big" icon="${icon}"></pl-icon> ` : ""}
<div class="stretch fit-horizontally ${icon ? "left-margined" : ""}">
<div class="bold large bottom-half-margined">${dialogTitle}</div>
<div style="word-break: break-word;">${message}</div>
</div>
</div>
<div class="spacer"></div>
`
: ""}
<div
class="${vertical || options.length > 2 ? "vertical" : "horizontal stretching"} spacing layout"
>
${options.map(
(o: any, i: number) =>
html`
<pl-button class="${this._buttonClass(i)}" @click=${() => this.done(i)}>
${o}
</pl-button>
`
)}
</div>
</div>
</div>
`;
}
done(i: number = -1) {
super.done(i);
}
async show({
message = "",
title = "",
options = ["OK"],
type = "info",
preventDismiss = false,
vertical = false,
icon = this._icon(type),
preventAutoClose,
maxWidth,
width,
hideOnDocumentVisibilityChange = false,
}: AlertOptions = {}): Promise<number> {
this.message = message;
this.dialogTitle = title;
this.type = type;
this.preventDismiss = preventDismiss;
this.options = options;
this.vertical = vertical;
this.icon = icon;
if (typeof preventAutoClose !== "undefined") {
this.preventAutoClose = preventAutoClose;
}
this.maxWidth = maxWidth;
this.width = width;
const promise = super.show();
await this.updateComplete;
this._inner.style.setProperty("--pl-dialog-max-width", maxWidth || "inherit");
this._inner.style.setProperty("--pl-dialog-width", width || "inherit");
if (hideOnDocumentVisibilityChange) {
document.addEventListener("visibilitychange", () => this.done(), { once: true });
}
return promise;
}
private _icon(type: string) {
switch (type) {
case "info":
return "info-round";
case "warning":
return "error";
case "success":
return "success";
case "question":
case "choice":
case "destructive":
return "question";
default:
return "";
}
}
private _buttonClass(i: number) {
if (i === 0) {
return this.type === "destructive" ? "negative" : this.type !== "choice" ? "primary" : "";
} else {
return "";
}
}
}