215 lines
5.5 KiB
JavaScript
215 lines
5.5 KiB
JavaScript
|
/**
|
||
|
@license
|
||
|
Copyright (c) 2023 EDV Wasmeier
|
||
|
*/
|
||
|
|
||
|
import { EventHelpers } from '@tp/helpers/event-helpers.js';
|
||
|
import { LitElement, html, css, svg } from 'lit';
|
||
|
|
||
|
class TpToast extends EventHelpers(LitElement) {
|
||
|
static get styles() {
|
||
|
return [
|
||
|
css`
|
||
|
:host {
|
||
|
--tp-toast-info-icon-color: #fff;
|
||
|
--tp-toast-success-icon-color: #fff;
|
||
|
--tp-toast-error-icon-color: #fff;
|
||
|
--tp-toast-warning-icon-color: #fff;
|
||
|
transition: transform 0.5s, opacity 0.3s;
|
||
|
will-change: transform, opacity;
|
||
|
display: inline-block;
|
||
|
border-radius: 2px;
|
||
|
background: #FAFAFA;
|
||
|
font-size: 0.8em;
|
||
|
cursor: pointer;
|
||
|
opacity: 1;
|
||
|
box-shadow: 0 3px 4px 0 rgba(0, 0, 0, 0.14),
|
||
|
0 1px 8px 0 rgba(0, 0, 0, 0.12),
|
||
|
0 3px 3px -2px rgba(0, 0, 0, 0.4);
|
||
|
}
|
||
|
|
||
|
.wrap {
|
||
|
display: flex;
|
||
|
flex-direction: row;
|
||
|
}
|
||
|
|
||
|
.icon {
|
||
|
padding: 10px;
|
||
|
border-right: 1px #fff;
|
||
|
color: #ffffff;
|
||
|
display: flex;
|
||
|
flex-direction: row;
|
||
|
align-items: center;
|
||
|
justify-content: center;
|
||
|
}
|
||
|
|
||
|
:host([type="info"]) .icon {
|
||
|
background: #039BE5;
|
||
|
}
|
||
|
|
||
|
:host([type="success"]) .icon {
|
||
|
background: #558B2F;
|
||
|
color: #fff;
|
||
|
}
|
||
|
|
||
|
:host([type="warning"]) .icon {
|
||
|
background: #FFCA28;
|
||
|
}
|
||
|
|
||
|
:host([type="error"]) .icon {
|
||
|
background: #B71C1C;
|
||
|
}
|
||
|
|
||
|
.content {
|
||
|
padding: 10px 10px 10px 10px;
|
||
|
line-height: 24px;
|
||
|
}
|
||
|
|
||
|
.dismiss {
|
||
|
display: flex;
|
||
|
align-items: center;
|
||
|
justify-content: center;
|
||
|
padding: 10px;
|
||
|
}
|
||
|
|
||
|
.dismiss tp-icon {
|
||
|
--tp-icon-width: 18px;
|
||
|
--tp-icon-height: 18px;
|
||
|
}
|
||
|
`
|
||
|
];
|
||
|
}
|
||
|
|
||
|
render() {
|
||
|
const { sticky, dismissIcon, icon } = this;
|
||
|
|
||
|
return html`
|
||
|
<div class="wrap">
|
||
|
<div class="icon">
|
||
|
<tp-icon .icon=${icon || TpToast[this.type + 'Icon']}></tp-icon>
|
||
|
</div>
|
||
|
<div class="content">
|
||
|
<slot></slot>
|
||
|
</div>
|
||
|
${!sticky ? html`
|
||
|
<div class="dismiss">
|
||
|
<tp-icon .icon=${dismissIcon || TpToast.defaultDismissIcon} @click=${this.dismiss}></tp-icon>
|
||
|
</div>
|
||
|
` : null}
|
||
|
</div>
|
||
|
`;
|
||
|
}
|
||
|
|
||
|
static get properties() {
|
||
|
return {
|
||
|
icon: { type: Object },
|
||
|
dismissIcon: { type: Object },
|
||
|
|
||
|
/**
|
||
|
* Configures what kind of toast this is.
|
||
|
* Comes with the variants `info`, `warning`, `error`, `success`.
|
||
|
* Each type comes with a pre-defined icon, but via the `icon` property a custom icon can also be used.
|
||
|
*/
|
||
|
type: { type: String, reflect: true },
|
||
|
|
||
|
/**
|
||
|
* Delay till the toast dismisses itself.
|
||
|
* Set in milliseconds.
|
||
|
*/
|
||
|
delay: { type: Number },
|
||
|
|
||
|
/**
|
||
|
* If true, the toast cant be dismissed manually.
|
||
|
*/
|
||
|
sticky: { type: Boolean },
|
||
|
|
||
|
isDismissed: { type: Boolean },
|
||
|
|
||
|
translateY: { type: Number },
|
||
|
};
|
||
|
}
|
||
|
|
||
|
constructor() {
|
||
|
super();
|
||
|
this.type = 'info';
|
||
|
this.delay = 5000;
|
||
|
this.translateY = -150;
|
||
|
this.dismiss = this.dismiss.bind(this);
|
||
|
}
|
||
|
|
||
|
static get defaultDismissIcon() {
|
||
|
return svg`<path fill="var(--tp-toast-dismiss-icon-color)" d="M20 6.91L17.09 4L12 9.09L6.91 4L4 6.91L9.09 12L4 17.09L6.91 20L12 14.91L17.09 20L20 17.09L14.91 12L20 6.91Z" />`;
|
||
|
}
|
||
|
|
||
|
static get infoIcon() {
|
||
|
return svg`<path fill="var(--tp-toast-info-icon-color)" d="M13,9H11V7H13M13,17H11V11H13M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2Z" />`;
|
||
|
}
|
||
|
|
||
|
static get successIcon() {
|
||
|
return svg`<path fill="var(--tp-toast-success-icon-color)" d="M10,17L5,12L6.41,10.58L10,14.17L17.59,6.58L19,8M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2Z" />`;
|
||
|
}
|
||
|
|
||
|
static get errorIcon() {
|
||
|
return svg`<path fill="var(--tp-toast-error-icon-color)" d="M12,2C17.53,2 22,6.47 22,12C22,17.53 17.53,22 12,22C6.47,22 2,17.53 2,12C2,6.47 6.47,2 12,2M15.59,7L12,10.59L8.41,7L7,8.41L10.59,12L7,15.59L8.41,17L12,13.41L15.59,17L17,15.59L13.41,12L17,8.41L15.59,7Z" />`;
|
||
|
}
|
||
|
|
||
|
static get warningIcon() {
|
||
|
return svg`<path fill="var(--tp-toast-warning-icon-color)" d="M13 14H11V9H13M13 18H11V16H13M1 21H23L12 2L1 21Z" />`;
|
||
|
}
|
||
|
|
||
|
connectedCallback() {
|
||
|
super.connectedCallback();
|
||
|
this.listen(this, 'transitionend', '_afterTransitioned');
|
||
|
}
|
||
|
|
||
|
shouldUpdate(changes) {
|
||
|
if (changes.has('translateY')) {
|
||
|
this._translateYChanged();
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Dismiss the toast right now.
|
||
|
*/
|
||
|
dismiss() {
|
||
|
this.isDismissed = true;
|
||
|
this.style.transform = `translate3d(200px, ${this.translateY}px, 0px)`;
|
||
|
this.style.opacity = '0';
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Starts the delay timeout to dismiss the toast automatically.
|
||
|
*/
|
||
|
activateDelay() {
|
||
|
if (this.delay > 0) {
|
||
|
this.stopDelay();
|
||
|
this._delayJob = setTimeout(this.dismiss, this.delay);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
stopDelay() {
|
||
|
if (this._delayJob) {
|
||
|
clearTimeout(this._delayJob);
|
||
|
this._delayJob = null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_afterTransitioned(e) {
|
||
|
if (this.isDismissed) {
|
||
|
if (e.propertyName === 'opacity') {
|
||
|
this.dispatchEvent(new CustomEvent('toast-dismissed', { detail: { toast: this } , bubbles: true, composed: true }));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_translateYChanged() {
|
||
|
if (this.isDismissed) {
|
||
|
return;
|
||
|
}
|
||
|
this.style.transform = `translate3d(0px, ${this.translateY}px, 0px)`;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
window.customElements.define('tp-toast', TpToast);
|