UNPKG

@panoramax/web-viewer

Version:

Panoramax web viewer for geolocated pictures

204 lines (182 loc) 5.62 kB
import { LitElement, html, css, unsafeCSS, nothing } from "lit"; import LogoDead from "../../img/logo_dead.svg"; import LoaderBg from "../../img/loader_base.jpg"; /** * Loader component display a full page covering for user waiting. * @class Panoramax.components.ui.Loader * @element pnx-loader * @extends [lit.LitElement](https://lit.dev/docs/api/LitElement/) * @example * ```html * <pnx-loader ._parent=${this.viewer} /> * ``` */ export default class Loader extends LitElement { /** @private */ static styles = css` :host { position: absolute; inset: 0; display: flex; flex-direction: column; justify-content: center; gap: 10px; align-items: center; background-image: url('${unsafeCSS(LoaderBg)}'); background-repeat: no-repeat; background-size: cover; background-position: center; z-index: 200; font-family: var(--font-family); font-weight: 550; color: var(--black); font-size: 1.4em; text-align: center; visibility: hidden; opacity: 0; } :host(*[visible]) { visibility: visible; opacity: 1; } div.label { background-color: rgba(255,255,255,0.5); box-shadow: white 0 0 10px; padding: 3px 10px; border-radius: 50px; } /* Logo */ img.logo-dead { width: 100px; } `; /** * Component properties. * @memberof Panoramax.components.ui.Loader# * @type {Object} * @property {boolean} [visible=true] Is the loader visible to user ? * @property {boolean} [no-label=false] Set to true to avoid loading label display * @property {number} [value] Progress bar percentage (0-100) */ static properties = { _mode: {state: true}, _label: {state: true}, _isLabelFun: {state: true}, visible: {type: Boolean, reflect: true}, "no-label": {type: Boolean}, value: {type: Number}, }; constructor() { super(); this.visible = true; this["no-label"] = false; this._mode = "loading"; this._isLabelFun = false; this.value = -1; } /** @private */ connectedCallback() { super.connectedCallback(); this._nextLabel(); } /** * Is the loader currently visible ? * @returns {boolean} True if visible * @memberof Panoramax.components.ui.Loader# */ isVisible() { return this.visible; } /** * Dismiss loader, or show error * @param {object} [err] Optional error object to show in browser console * @param {str} [errMeaningful] Optional error message to show to user * @param {fct} [next] Optional function to run after loader dismiss * @memberof Panoramax.components.ui.Loader# */ dismiss(err = null, errMeaningful = null, next = null) { clearTimeout(this._loaderLabelChanger); if(!err) { this.value = 100; this._mode = "done"; this.visible = false; this.style.transition = "all 0.5s"; setTimeout(() => this.parentNode.removeChild(this), 2000); /** * Event when component is ready to use. * This happens when loader screen disappear, with picture and map loaded. * * To follow more precisely loading steps, you can also watch for sub-components `ready` events. * ```js * // Watch API-readiness * viewer.addEventListener("api:ready", ...); // From parent * viewer.api.addEventListener("ready", ...); // Or on sub-component * ``` * @event Panoramax.components.core.Basic#ready * @type {CustomEvent} */ const readyEvt = new CustomEvent("ready"); this._parent.dispatchEvent(readyEvt); if(next) { next(); } } else { if(err !== true) { console.error(err); } let errHtml = [ this._parent?._t.pnx.error, html`<br />` ]; if(errMeaningful) { errHtml.push(errMeaningful, html`<br />`); } if(next) { errHtml.push(html`<pnx-button kind="full">${this._parent?._t.pnx.error_click}</pnx-button>`); this.addEventListener("click", next); } else { errHtml.push(html`<small>${this._parent?._t.pnx.error_retry}</small>`); } this._mode = "error"; this._label = errHtml; const errLabel = errMeaningful || "Panoramax JS had a blocking exception"; /** * Event for viewer failing to initially load. * * To follow more precisely loading failures, you can also watch for sub-components `broken` events. * ```js * // Watch API breaks * viewer.addEventListener("api:broken", ...); // From parent * viewer.api.addEventListener("broken", ...); // Or on sub-component * ``` * @event Panoramax.components.core.Basic#broken * @type {CustomEvent} * @property {string} detail.error The user-friendly error message to display */ const brokenEvt = new CustomEvent("broken", { detail: { error: errLabel } }); this._parent.dispatchEvent(brokenEvt); // Throw error throw new Error(errLabel); } } /** @private */ _nextLabel() { if(!this._isLabelFun) { this._label = this._parent?._t.pnx.loading_labels_serious[ Math.floor(Math.random() * this._parent?._t.pnx.loading_labels_serious.length) ]; this._isLabelFun = true; } else { this._label = this._parent?._t.pnx.loading_labels_fun[ Math.floor(Math.random() * this._parent?._t.pnx.loading_labels_fun.length) ]; } this._loaderLabelChanger = setTimeout(this._nextLabel.bind(this), 500 + Math.random() * 1000); } /** @private */ render() { if(this._mode == "error") { return html` <img class="logo-dead" src=${LogoDead} alt="" title=${this._parent?._t.map.loading} /> ${this._label} `; } return html` <pnx-progress-bar .value=${this.value}></pnx-progress-bar> ${this["no-label"] ? nothing : html`<div class="label">${this._label}</div>`} `; } } customElements.define("pnx-loader", Loader);