UNPKG

sunshine-layx

Version:

A magical window.

261 lines (206 loc) 9.19 kB
import UIControl from "../basic/interfaces/UIControl"; import UIComponent from "../basic/models/UIComponent"; import App from "../core/App"; import UIIcon from "./UIIcon"; import * as Types from "../../types"; import * as ElementHelper from "../utils/ElementHelper"; import * as CastHelper from "../utils/CastHelper"; import * as Enums from "../basic/enums"; import * as TimeHelper from "../utils/TimeHelper"; import * as EventHelper from "../utils/EventHelper"; export default class UINotice extends UIComponent implements UIControl { private timer: number = 0; private fps: number = 10; public static readonly topOffset: number = 24; public static readonly space: number = 10; public readonly id: number = this.app.noticeZIndex; public readonly elementId: string = `${this.app.prefix + Enums.ComponentType.NOTICE}-${this.id}`; public type: Enums.NoticeType = Enums.NoticeType.INFO; public title: string | undefined; public message: string; public timeout: number = 3000; private _element: HTMLElement | null = null; get element() { return document.getElementById(this.elementId); } private _processElement: HTMLElement | null = null; get processElement() { if (!this.element) return null; return this.element.querySelector<HTMLElement>(`.${this.app.prefix + Enums.ComponentType.NOTICE}-process`); } constructor(app: App, options: Types.NoticeOption) { super(app); this.type = CastHelper.noticeTypeCast(options.type, this.type); this.title = CastHelper.stringOrUndefinedCast(options.title); this.message = CastHelper.stringCast(options.message); this.timeout = CastHelper.numberCast(options.timeout, this.timeout); } present(): DocumentFragment { const fragment = ElementHelper.createFragment(); const noticeElement = ElementHelper.createElement("div"); noticeElement.id = this.elementId; ElementHelper.addClasses(noticeElement, this.app.prefix, Enums.ComponentType.NOTICE, "animate-d3s", "animate-fade-in-right" ); ElementHelper.addStyles(noticeElement, <Types.CSSStyleObject>{ zIndex: `${this.id}`, }); this.bindEvent(noticeElement); this.createClose(noticeElement); const noticeContainerElement = ElementHelper.createElement("div"); ElementHelper.addClasses(noticeContainerElement, this.app.prefix, `${Enums.ComponentType.NOTICE}-container`, "flexbox", "flex-row" ); const noticeIconElement = ElementHelper.createElement("div"); ElementHelper.addClasses(noticeIconElement, this.app.prefix, `${Enums.ComponentType.NOTICE}-icon`, `${Enums.ComponentType.NOTICE}-${this.type}` ); const icon = new UIIcon(this.app, `${this.type}`); const iconElement = icon.present(); noticeIconElement.appendChild(iconElement); noticeContainerElement.appendChild(noticeIconElement); const noticeMessageElement = ElementHelper.createElement("div"); ElementHelper.addClasses(noticeMessageElement, this.app.prefix, `${Enums.ComponentType.NOTICE}-message`, "flex-item" ); this.createTitle(noticeMessageElement); this.createContent(noticeMessageElement); noticeContainerElement.appendChild(noticeMessageElement); noticeElement.appendChild(noticeContainerElement); this.createNoticeTime(noticeElement); this.createProcess(noticeElement); fragment.appendChild(noticeElement); return fragment; } private createClose(noticeElement: HTMLElement) { const closeElement = ElementHelper.createElement("div"); ElementHelper.addClasses(closeElement, this.app.prefix, `${Enums.ComponentType.NOTICE}-close-button`, `flexbox`, `flex-center` ); const icon = new UIIcon(this.app, 'destroy'); const iconElement = icon.present(); closeElement.appendChild(iconElement); closeElement.addEventListener("mousedown", (ev: MouseEvent) => { this.destroy(); }); noticeElement.appendChild(closeElement); } private createTitle(noticeElement: HTMLElement) { if (this.title !== undefined) { const titleElement = ElementHelper.createElement("div"); ElementHelper.addClasses(titleElement, this.app.prefix, `${Enums.ComponentType.NOTICE}-title`, `${Enums.ComponentType.NOTICE}-${this.type}` ); titleElement.innerText = this.title; noticeElement.appendChild(titleElement); } } private createContent(noticeElement: HTMLElement) { const contentELement = ElementHelper.createElement("div"); ElementHelper.addClasses(contentELement, this.app.prefix, `${Enums.ComponentType.NOTICE}-content` ); contentELement.innerText = this.message; noticeElement.appendChild(contentELement); } private createNoticeTime(noticeElement: HTMLElement) { const timeElement = ElementHelper.createElement("div"); ElementHelper.addClasses(timeElement, this.app.prefix, `${Enums.ComponentType.NOTICE}-time` ); timeElement.innerText = `${TimeHelper.getDatetime()}`; noticeElement.appendChild(timeElement); } private createProcess(noticeElement: HTMLElement) { const processElement = ElementHelper.createElement("div"); ElementHelper.addClasses(processElement, this.app.prefix, `${Enums.ComponentType.NOTICE}-process` ); noticeElement.appendChild(processElement); } private calcTopOffset(index: number | undefined = undefined): number { let currentTop = UINotice.topOffset; if (this.app.notices.length > 0) { const step = (index === undefined ? this.app.notices.length : index) - 1; const lastNotice = this.app.notices[step]; const lastNoticeElement = lastNotice.element; const clientRect = lastNoticeElement!.getBoundingClientRect(); currentTop = clientRect.top + clientRect.height + UINotice.space; } return currentTop; } private bindEvent(noticeElement: HTMLElement) { noticeElement.addEventListener("animationend", (ev: AnimationEvent) => { const element = this.element; if (ElementHelper.containClass(element, this.app.prefix, "animate-fade-in-right-reverse")) { this.remove(); } if (this.timeout !== 0) { if (ElementHelper.containClass(element, this.app.prefix, "animate-fade-in-right")) { this.processAnimate(); } } ElementHelper.removeClasses(element, this.app.prefix, "animate-fade-in-right-reverse", "animate-fade-in-right", "animate-slide-to-top" ); }); if (this.timeout !== 0) { noticeElement.addEventListener("mouseenter", (ev: MouseEvent) => { clearInterval(this.timer); }); noticeElement.addEventListener("mouseleave", (ev: MouseEvent) => { this.processAnimate(); }); } } public destroy(): void { ElementHelper.addClasses(this.element, this.app.prefix, "animate-fade-in-right-reverse" ); } private remove(): void { const index = this.app.notices.indexOf(this); this.app.notices.splice(index, 1); ElementHelper.removeElement(this.element); this.updateOffset(index); } private processAnimate() { const handler: TimerHandler = () => { if (this.fps <= this.timeout) { ElementHelper.addStyles(this.processElement, <Types.CSSStyleObject>{ width: `${(this.fps / this.timeout) * 100}%` }); } else { clearInterval(this.timer); this.destroy(); } this.fps += 10; }; this.timer = setInterval(handler, 10); } public updateOffset(index: number, isCreate: boolean = false): void { const notices = this.app.notices; for (let i = index; i < notices.length; i++) { ElementHelper.addStyles(notices[i].element, <Types.CSSStyleObject>{ top: `${i === 0 ? UINotice.topOffset : this.calcTopOffset(i)}px` }); if (!isCreate) { ElementHelper.addClasses(this.element, this.app.prefix, "animate-slide-to-top" ); } } } }