136 строки
3.2 KiB
TypeScript
136 строки
3.2 KiB
TypeScript
|
import {LitElement, css, html} from 'lit';
|
||
|
import {SHARED_STYLES} from '../css/shared-css.js';
|
||
|
import {customElement, state, property} from 'lit/decorators.js';
|
||
|
|
||
|
const DEFAULT_DURATION = 7000;
|
||
|
|
||
|
@customElement('chromedash-toast')
|
||
|
class ChromedashToast extends LitElement {
|
||
|
@property({type: String})
|
||
|
msg = '';
|
||
|
|
||
|
@property({type: Boolean, reflect: true})
|
||
|
open = false;
|
||
|
|
||
|
@property({attribute: false})
|
||
|
actionLabel = '';
|
||
|
|
||
|
@state()
|
||
|
currentTimeout: number | null = null;
|
||
|
|
||
|
@property({type: Boolean})
|
||
|
waitingForTransition = false;
|
||
|
|
||
|
static get styles() {
|
||
|
return [
|
||
|
...SHARED_STYLES,
|
||
|
css`
|
||
|
:host {
|
||
|
display: flex;
|
||
|
justify-content: space-between;
|
||
|
position: fixed;
|
||
|
background: var(--toast-background);
|
||
|
color: var(--toast-color);
|
||
|
min-height: 48px;
|
||
|
min-width: 288px;
|
||
|
padding: var(--content-padding);
|
||
|
box-sizing: border-box;
|
||
|
box-shadow: var(--card-box-shadow);
|
||
|
border-radius: var(--border-radius);
|
||
|
margin: var(--content-padding);
|
||
|
cursor: default;
|
||
|
transition:
|
||
|
transform 0.3s,
|
||
|
opacity 0.3s;
|
||
|
opacity: 0;
|
||
|
will-change: opacity, transform;
|
||
|
-webkit-transform: translateY(100px);
|
||
|
transform: translateY(100px);
|
||
|
z-index: 3;
|
||
|
bottom: 0;
|
||
|
}
|
||
|
|
||
|
:host([open]) {
|
||
|
opacity: 1;
|
||
|
transform: translateY(-32px);
|
||
|
}
|
||
|
|
||
|
#action {
|
||
|
text-transform: uppercase;
|
||
|
text-decoration: none;
|
||
|
color: var(--toast-action-color);
|
||
|
font-weight: bold;
|
||
|
}
|
||
|
|
||
|
#msg {
|
||
|
margin-right: var(--content-padding);
|
||
|
}
|
||
|
`,
|
||
|
];
|
||
|
}
|
||
|
|
||
|
showMessage(
|
||
|
msg: string,
|
||
|
optAction: string,
|
||
|
optTapHandler: () => void,
|
||
|
optDuration: number
|
||
|
) {
|
||
|
if (this.waitingForTransition) return;
|
||
|
|
||
|
this.msg = msg;
|
||
|
this.actionLabel = optAction;
|
||
|
|
||
|
if (optTapHandler) {
|
||
|
const action = this.shadowRoot!.querySelector('#action');
|
||
|
if (action) {
|
||
|
action.addEventListener(
|
||
|
'click',
|
||
|
e => {
|
||
|
e.preventDefault();
|
||
|
optTapHandler();
|
||
|
},
|
||
|
{once: true}
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (this.open) {
|
||
|
// triggers the previous toast to slide out
|
||
|
this.open = false;
|
||
|
this.waitingForTransition = true;
|
||
|
if (this.currentTimeout !== null) {
|
||
|
clearTimeout(this.currentTimeout);
|
||
|
}
|
||
|
// Don't show the new toast until the transition is over
|
||
|
// (wait for the previous toast to be completely gone)
|
||
|
this.addEventListener(
|
||
|
'transitionend',
|
||
|
() => {
|
||
|
this.show(optDuration);
|
||
|
this.waitingForTransition = false;
|
||
|
},
|
||
|
{once: true}
|
||
|
);
|
||
|
} else {
|
||
|
this.show(optDuration);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
show(optDuration) {
|
||
|
const duration = optDuration || DEFAULT_DURATION;
|
||
|
if (duration > 0) {
|
||
|
this.currentTimeout = window.setTimeout(() => {
|
||
|
this.open = false;
|
||
|
}, duration);
|
||
|
}
|
||
|
this.open = true;
|
||
|
}
|
||
|
|
||
|
render() {
|
||
|
return html`
|
||
|
<span id="msg">${this.msg}</span>
|
||
|
<a href="#" id="action">${this.actionLabel}</a>
|
||
|
`;
|
||
|
}
|
||
|
}
|