web-ui-pack
Version:
Web package with UI elements
302 lines (301 loc) • 10.1 kB
JavaScript
import WUPBaseModal from "./baseModal";
import { px2Number } from "./helpers/styleHelpers";
export var NotifyOpenCases;
(function (NotifyOpenCases) {
NotifyOpenCases[NotifyOpenCases["onManualCall"] = 0] = "onManualCall";
NotifyOpenCases[NotifyOpenCases["onInit"] = 1] = "onInit";
})(NotifyOpenCases || (NotifyOpenCases = {}));
export var NotifyCloseCases;
(function (NotifyCloseCases) {
NotifyCloseCases[NotifyCloseCases["onManualCall"] = 0] = "onManualCall";
NotifyCloseCases[NotifyCloseCases["onCloseClick"] = 1] = "onCloseClick";
NotifyCloseCases[NotifyCloseCases["onTimeEnd"] = 2] = "onTimeEnd";
})(NotifyCloseCases || (NotifyCloseCases = {}));
const tagName = "wup-notify";
export default class WUPNotifyElement extends WUPBaseModal {
#ctr = this.constructor;
static get observedOptions() {
return ["autoClose", "closeOnClick", "openCase", "placement", "selfRemove"];
}
static get observedAttributes() {
return super.observedAttributes;
}
static get $styleRoot() {
return `:root {
--notify-anim-t: 400ms;
--notify-margin: 1em 0;
--notify-w: 300px;
--notify-text: #fff;
--notify-bg: rgba(16,70,82,0.9);
--notify-shadow: #0003;
--notify-progress: #009fbc;
}
[wupdark] {
--notify-text: #d8d8d8;
--notify-bg: rgba(16,70,82,0.9);
--notify-shadow: #0006;
}`;
}
static get $style() {
return `${super.$style}
:host {
--modal-anim: var(--notify-anim-t) cubic-bezier(0,0,0.2,1) 0ms;
z-index: 9010;
min-height: 64px;
max-height: 80vh;
width: var(--notify-w);
max-width: 80wv;
margin: var(--notify-margin);
color: var(--notify-text);
background: var(--notify-bg);
box-shadow: 0 1px 4px 0 var(--notify-shadow);
}
:host[w-placement=top-left],
:host[w-placement=bottom-left] {
border-radius: 0 var(--border-radius) var(--border-radius) 0;
left: 0;
transform: translateX(-100%);
}
:host[w-placement=top-right],
:host[w-placement=bottom-right] {
border-radius: var(--border-radius) 0 0 var(--border-radius);
right: 0;
transform: translateX(100%);
}
:host[w-placement=top-middle],
:host[w-placement="bottom-middle"] {
margin-left: auto;
margin-right: auto;
left:0; right:0;
transform: translateY(-100%);
}
:host[w-placement=top-left],
:host[w-placement=top-middle],
:host[w-placement=top-right] {
top:0;
}
:host[w-placement=bottom-left],
:host[w-placement=bottom-middle],
:host[w-placement=bottom-right] {
bottom:0;
}
:host[show] {
transform: none;
}
:host > button[close] {
--icon-img: var(--wup-icon-cross);
--icon: var(--notify-text);
z-index: 10;
position: absolute;
right:0; top:0;
margin: 0.4em;
}
:host > [progress] {
position: absolute;
bottom:0; left:0;
width: 100%;
height: 5px;
background: var(--notify-progress);
transform-origin: left;
}
`;
}
static $defaults = {
openCase: 1,
placement: "bottom-left",
autoClose: 5000,
closeOnClick: false,
selfRemove: true,
pauseOnHover: true,
pauseOnWinBlur: true,
};
static $show(opts) {
const me = document.createElement(tagName);
opts.className && me.classList.add(opts.className);
me.$options.selfRemove = true;
Object.assign(me.$options, opts.defaults);
me.$options.openCase = 1;
me.textContent = opts.textContent;
document.body.appendChild(me);
opts.onRender?.(me);
return me;
}
$refClose;
$refProgress;
$isPaused = false;
$pause() {
this.#isPlayed = false;
return this.#pauseRef?.pause() ?? -1;
}
#pauseRef;
#isPlayed = false;
get $isPlayed() {
return this.#isPlayed;
}
$play(ms) {
if (!ms) {
ms = this.#pauseRef?.leftPlayTime ?? (this._opts.autoClose || 0);
}
this.#isPlayed = true;
const now = Date.now();
const t1 = setTimeout(() => {
this.goClose(2, null);
}, ms);
const anim = this.#pauseRef?.anim ??
this.$refProgress.animate([{ transform: "scaleX(100%)" }, { transform: "scaleX(0)" }], {
fill: "forwards",
duration: ms - 100,
});
anim.play();
const msConst = ms;
this.#pauseRef = {
anim,
pause: () => {
clearTimeout(t1);
anim.pause();
const left = msConst - Date.now() + now;
this.#pauseRef.leftPlayTime = left;
return left;
},
};
}
gotRender(isOpening = false) {
if (!isOpening) {
return;
}
this.setAttribute("role", "alert");
this.setAttribute("w-placement", this._opts.placement);
if (!this.$refClose) {
this.$refClose = document.createElement("button");
const q = this.$refClose;
q.type = "button";
q.setAttribute("aria-label", __wupln("close", "aria"));
q.setAttribute(this.#ctr.classNameBtnIcon, "");
q.setAttribute("close", "");
this.prepend(q);
q.onclick = (e) => this.goClose(1, e);
}
if (!this.$refProgress && this._opts.autoClose) {
this.$refProgress = document.createElement("div");
const q = this.$refProgress;
q.setAttribute("progress", "");
q.setAttribute("role", "progressbar");
this.appendChild(q);
}
}
gotChanges(propsChanged) {
super.gotChanges(propsChanged);
this._opts.openCase === 1 && this.goOpen(1, null);
}
_dy = 0;
_dx = "";
gotOpen(openCase, e) {
this.gotRender(true);
this._opts.closeOnClick && this.appendEvent(this, "click", (ev) => this.gotClick(ev));
const curNum = this._openedItems.push(this);
if (curNum > 1) {
this._dx = getComputedStyle(this).transform;
const lastI = curNum - 2;
const prev = this._openedItems.findLast((a, i) => i <= lastI && this.isSameSibling(a));
if (prev) {
this.style.transition = "none";
this.refreshVertical([prev, this], true);
const savedState = this.style.transform;
this.style.transform = `${this._dx} translateY(${this._dy}px)`;
setTimeout(() => {
this.style.transition = "";
this.style.transform = savedState;
});
}
}
const ms = this._opts.autoClose;
if (ms) {
let allowByFocus = true;
if (this._opts.pauseOnWinBlur) {
this.appendEvent(window, "blur", () => this.$pause());
this.appendEvent(window, "focus", () => this.$play());
allowByFocus = document.hasFocus();
}
allowByFocus && this.$play(ms);
if (this._opts.pauseOnHover) {
this.appendEvent(this, "mouseenter", () => {
const left = this.$pause();
this.appendEvent(this, "mouseleave", () => this.$play(left), { once: true });
});
this.appendEvent(this, "touchstart", () => {
this.$isPlayed ? this.$pause() : this.$play();
});
}
}
}
isSameSibling(item) {
return item._opts.placement === this._opts.placement && item.offsetHeight > 0;
}
refreshVertical(items, isInit = false) {
let isBottom = false;
switch (this._opts.placement) {
case "bottom-left":
case "bottom-middle":
case "bottom-right":
isBottom = true;
break;
default:
break;
}
let prev;
if (!items)
items = this._openedItems.filter((x) => this.isSameSibling(x));
const { marginBottom, marginTop } = getComputedStyle(this);
items.forEach((a) => {
const isNext = !!prev;
if (isNext) {
const height = prev.offsetHeight;
a._dy = Math.round(isBottom
? prev._dy - height - px2Number(marginBottom)
: prev._dy + height + px2Number(marginTop));
a.style.transform = `translateY(${a._dy}px)`;
}
else if (!isInit) {
a.style.transform = "";
a._dy = 0;
}
prev = a;
});
}
gotClose(closeCase, ev) {
this.style.transform = this._dx ? `${this._dx} translateY(${this._dy}px)` : "";
this.disposeEvents();
}
resetState() {
super.resetState();
this.style.transform = "";
const i = this._openedItems.indexOf(this);
if (i > -1) {
this._openedItems.splice(i, 1);
this.refreshVertical();
}
this.isConnected && this._opts.selfRemove && this.remove();
}
gotClick(e) {
const t = e.target;
if (e.defaultPrevented || (t === this && !this._opts.closeOnClick) || e.button) {
return;
}
if (this._opts.closeOnClick) {
this.goClose(1, e);
return;
}
const all = this.querySelectorAll("[close]").values();
for (const el of all) {
if (this.itsMe.call(el, t)) {
this.goClose(1, e);
return;
}
}
}
get _openedItems() {
return __openedItems;
}
}
const __openedItems = [];
customElements.define(tagName, WUPNotifyElement);