@vaadin/common-frontend
Version:
Vaadin frontend utils used by Flow and Hilla
439 lines (429 loc) • 14.6 kB
JavaScript
/*
* Copyright 2000-2020 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
import { __decorate } from "tslib";
import { html, LitElement } from 'lit';
import { property } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js';
import { ConnectionState } from './ConnectionState.js';
const DEFAULT_STYLE_ID = 'css-loading-indicator';
/**
* The loading indicator states
*/
export var LoadingBarState;
(function (LoadingBarState) {
LoadingBarState["IDLE"] = "";
LoadingBarState["FIRST"] = "first";
LoadingBarState["SECOND"] = "second";
LoadingBarState["THIRD"] = "third";
})(LoadingBarState || (LoadingBarState = {}));
/**
* Component showing loading and connection indicator. When added to DOM,
* listens for changes on `window.Vaadin.connectionState` ConnectionStateStore.
*/
export class ConnectionIndicator extends LitElement {
/**
* Initialize global connection indicator instance at
* window.Vaadin.connectionIndicator and add instance to the document body.
*/
static create() {
var _a, _b;
const $wnd = window;
if (!((_a = $wnd.Vaadin) === null || _a === void 0 ? void 0 : _a.connectionIndicator)) {
$wnd.Vaadin || ($wnd.Vaadin = {});
$wnd.Vaadin.connectionIndicator = document.createElement('vaadin-connection-indicator');
document.body.appendChild($wnd.Vaadin.connectionIndicator);
}
return (_b = $wnd.Vaadin) === null || _b === void 0 ? void 0 : _b.connectionIndicator;
}
constructor() {
super();
/**
* The delay before showing the loading indicator, in ms.
*/
this.firstDelay = 450;
/**
* The delay before the loading indicator goes into "second" state, in ms.
*/
this.secondDelay = 1500;
/**
* The delay before the loading indicator goes into "third" state, in ms.
*/
this.thirdDelay = 5000;
/**
* The duration for which the connection state change message is visible,
* in ms.
*/
this.expandedDuration = 2000;
/**
* The message shown when the connection goes to connected state.
*/
this.onlineText = 'Online';
/**
* The message shown when the connection goes to lost state.
*/
this.offlineText = 'Connection lost';
/**
* The message shown when the connection goes to reconnecting state.
*/
this.reconnectingText = 'Connection lost, trying to reconnect...';
this.offline = false;
this.reconnecting = false;
this.expanded = false;
this.loading = false;
this.loadingBarState = LoadingBarState.IDLE;
this.applyDefaultThemeState = true;
this.firstTimeout = 0;
this.secondTimeout = 0;
this.thirdTimeout = 0;
this.expandedTimeout = 0;
this.lastMessageState = ConnectionState.CONNECTED;
this.connectionStateListener = () => {
this.expanded = this.updateConnectionState();
this.expandedTimeout = this.timeoutFor(this.expandedTimeout, this.expanded, () => {
this.expanded = false;
}, this.expandedDuration);
};
}
render() {
return html `
<div class="v-loading-indicator ${this.loadingBarState}" style=${this.getLoadingBarStyle()}></div>
<div
class="v-status-message ${classMap({
active: this.reconnecting,
})}"
>
<span class="text"> ${this.renderMessage()} </span>
</div>
`;
}
connectedCallback() {
var _a;
super.connectedCallback();
const $wnd = window;
if ((_a = $wnd.Vaadin) === null || _a === void 0 ? void 0 : _a.connectionState) {
this.connectionStateStore = $wnd.Vaadin.connectionState;
this.connectionStateStore.addStateChangeListener(this.connectionStateListener);
this.updateConnectionState();
}
this.updateTheme();
}
disconnectedCallback() {
super.disconnectedCallback();
if (this.connectionStateStore) {
this.connectionStateStore.removeStateChangeListener(this.connectionStateListener);
}
this.updateTheme();
}
get applyDefaultTheme() {
return this.applyDefaultThemeState;
}
set applyDefaultTheme(applyDefaultTheme) {
if (applyDefaultTheme !== this.applyDefaultThemeState) {
this.applyDefaultThemeState = applyDefaultTheme;
this.updateTheme();
}
}
createRenderRoot() {
return this;
}
/**
* Update state flags.
*
* @returns true if the connection message changes, and therefore a new
* message should be shown
*/
updateConnectionState() {
var _a;
const state = (_a = this.connectionStateStore) === null || _a === void 0 ? void 0 : _a.state;
this.offline = state === ConnectionState.CONNECTION_LOST;
this.reconnecting = state === ConnectionState.RECONNECTING;
this.updateLoading(state === ConnectionState.LOADING);
if (this.loading) {
// Entering loading state, do not show message
return false;
}
if (state !== this.lastMessageState) {
this.lastMessageState = state;
// Message changes, show new message
return true;
}
// Message did not change
return false;
}
updateLoading(loading) {
this.loading = loading;
this.loadingBarState = LoadingBarState.IDLE;
this.firstTimeout = this.timeoutFor(this.firstTimeout, loading, () => {
this.loadingBarState = LoadingBarState.FIRST;
}, this.firstDelay);
this.secondTimeout = this.timeoutFor(this.secondTimeout, loading, () => {
this.loadingBarState = LoadingBarState.SECOND;
}, this.secondDelay);
this.thirdTimeout = this.timeoutFor(this.thirdTimeout, loading, () => {
this.loadingBarState = LoadingBarState.THIRD;
}, this.thirdDelay);
}
renderMessage() {
if (this.reconnecting) {
return this.reconnectingText;
}
if (this.offline) {
return this.offlineText;
}
return this.onlineText;
}
updateTheme() {
if (this.applyDefaultThemeState && this.isConnected) {
if (!document.getElementById(DEFAULT_STYLE_ID)) {
const style = document.createElement('style');
style.id = DEFAULT_STYLE_ID;
style.textContent = this.getDefaultStyle();
document.head.appendChild(style);
}
}
else {
const style = document.getElementById(DEFAULT_STYLE_ID);
if (style) {
document.head.removeChild(style);
}
}
}
getDefaultStyle() {
return `
v-progress-start {
0% {
width: 0%;
}
100% {
width: 50%;
}
}
v-progress-delay {
0% {
width: 50%;
}
100% {
width: 90%;
}
}
v-progress-wait {
0% {
width: 90%;
height: 4px;
}
3% {
width: 91%;
height: 7px;
}
100% {
width: 96%;
height: 7px;
}
}
v-progress-wait-pulse {
0% {
opacity: 1;
}
50% {
opacity: 0.1;
}
100% {
opacity: 1;
}
}
.v-loading-indicator,
.v-status-message {
position: fixed;
z-index: 251;
left: 0;
right: auto;
top: 0;
background-color: var(--lumo-primary-color, var(--material-primary-color, blue));
transition: none;
}
.v-loading-indicator {
width: 50%;
height: 4px;
opacity: 1;
pointer-events: none;
animation: v-progress-start 1000ms 200ms both;
}
.v-loading-indicator[style*='none'] {
display: block !important;
width: 100%;
opacity: 0;
animation: none;
transition: opacity 500ms 300ms, width 300ms;
}
.v-loading-indicator.second {
width: 90%;
animation: v-progress-delay 3.8s forwards;
}
.v-loading-indicator.third {
width: 96%;
animation: v-progress-wait 5s forwards, v-progress-wait-pulse 1s 4s infinite backwards;
}
vaadin-connection-indicator[offline] .v-loading-indicator,
vaadin-connection-indicator[reconnecting] .v-loading-indicator {
display: none;
}
.v-status-message {
opacity: 0;
width: 100%;
max-height: var(--status-height-collapsed, 8px);
overflow: hidden;
background-color: var(--status-bg-color-online, var(--lumo-primary-color, var(--material-primary-color, blue)));
color: var(
--status-text-color-online,
var(--lumo-primary-contrast-color, var(--material-primary-contrast-color, #fff))
);
font-size: 0.75rem;
font-weight: 600;
line-height: 1;
transition: all 0.5s;
padding: 0 0.5em;
}
vaadin-connection-indicator[offline] .v-status-message,
vaadin-connection-indicator[reconnecting] .v-status-message {
opacity: 1;
background-color: var(--status-bg-color-offline, var(--lumo-shade, #333));
color: var(
--status-text-color-offline,
var(--lumo-primary-contrast-color, var(--material-primary-contrast-color, #fff))
);
background-image: repeating-linear-gradient(
45deg,
rgba(255, 255, 255, 0),
rgba(255, 255, 255, 0) 10px,
rgba(255, 255, 255, 0.1) 10px,
rgba(255, 255, 255, 0.1) 20px
);
}
vaadin-connection-indicator[reconnecting] .v-status-message {
animation: show-reconnecting-status 2s;
}
vaadin-connection-indicator[offline] .v-status-message:hover,
vaadin-connection-indicator[reconnecting] .v-status-message:hover,
vaadin-connection-indicator[expanded] .v-status-message {
max-height: var(--status-height, 1.75rem);
}
vaadin-connection-indicator[expanded] .v-status-message {
opacity: 1;
}
.v-status-message span {
display: flex;
align-items: center;
justify-content: center;
height: var(--status-height, 1.75rem);
}
vaadin-connection-indicator[reconnecting] .v-status-message span::before {
content: '';
width: 1em;
height: 1em;
border-top: 2px solid
var(--status-spinner-color, var(--lumo-primary-color, var(--material-primary-color, blue)));
border-left: 2px solid
var(--status-spinner-color, var(--lumo-primary-color, var(--material-primary-color, blue)));
border-right: 2px solid transparent;
border-bottom: 2px solid transparent;
border-radius: 50%;
box-sizing: border-box;
animation: v-spin 0.4s linear infinite;
margin: 0 0.5em;
}
v-spin {
100% {
transform: rotate(360deg);
}
}
`;
}
getLoadingBarStyle() {
switch (this.loadingBarState) {
case LoadingBarState.IDLE:
return 'display: none';
case LoadingBarState.FIRST:
case LoadingBarState.SECOND:
case LoadingBarState.THIRD:
return 'display: block';
default:
return '';
}
}
timeoutFor(timeoutId, enabled, handler, delay) {
if (timeoutId !== 0) {
window.clearTimeout(timeoutId);
}
return enabled ? window.setTimeout(handler, delay) : 0;
}
static get instance() {
return ConnectionIndicator.create();
}
}
__decorate([
property({ type: Number })
], ConnectionIndicator.prototype, "firstDelay", void 0);
__decorate([
property({ type: Number })
], ConnectionIndicator.prototype, "secondDelay", void 0);
__decorate([
property({ type: Number })
], ConnectionIndicator.prototype, "thirdDelay", void 0);
__decorate([
property({ type: Number })
], ConnectionIndicator.prototype, "expandedDuration", void 0);
__decorate([
property({ type: String })
], ConnectionIndicator.prototype, "onlineText", void 0);
__decorate([
property({ type: String })
], ConnectionIndicator.prototype, "offlineText", void 0);
__decorate([
property({ type: String })
], ConnectionIndicator.prototype, "reconnectingText", void 0);
__decorate([
property({ type: Boolean, reflect: true })
], ConnectionIndicator.prototype, "offline", void 0);
__decorate([
property({ type: Boolean, reflect: true })
], ConnectionIndicator.prototype, "reconnecting", void 0);
__decorate([
property({ type: Boolean, reflect: true })
], ConnectionIndicator.prototype, "expanded", void 0);
__decorate([
property({ type: Boolean, reflect: true })
], ConnectionIndicator.prototype, "loading", void 0);
__decorate([
property({ type: String })
], ConnectionIndicator.prototype, "loadingBarState", void 0);
__decorate([
property({ type: Boolean })
], ConnectionIndicator.prototype, "applyDefaultTheme", null);
if (customElements.get('vaadin-connection-indicator') === undefined) {
customElements.define('vaadin-connection-indicator', ConnectionIndicator);
}
/**
* The global connection indicator object. Its appearance and behavior can be
* configured via properties:
*
* connectionIndicator.firstDelay = 0;
* connectionIndicator.onlineText = 'The application is online';
*
* To avoid altering the appearance while the indicator is active, apply the
* configuration in your application 'frontend/index.ts' file.
*/
export const connectionIndicator = ConnectionIndicator.instance;
//# sourceMappingURL=ConnectionIndicator.js.map