UNPKG

@eclipse-scout/core

Version:
137 lines (121 loc) 4.64 kB
/* * Copyright (c) 2010, 2025 BSI Business Systems Integration AG * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 * which is available at https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 */ import {GlassPane, LoadingSupportOptions, scout, WidgetSupport} from '../index'; export class LoadingSupport extends WidgetSupport { loadingIndicatorDelay: number; withGlassPane: boolean; protected _$loadingIndicator: JQuery; protected _loadingIndicatorTimeoutId: number; protected _glassPane: GlassPane; protected _containerScrollHandler: (event: JQuery.ScrollEvent) => void; /** * @param options a mandatory options object */ constructor(options: LoadingSupportOptions) { super(options); this.loadingIndicatorDelay = scout.nvl(options.loadingIndicatorDelay, 250); // ms this.withGlassPane = scout.nvl(options.withGlassPane, false); this._$loadingIndicator = null; this._loadingIndicatorTimeoutId = null; this._containerScrollHandler = this._onContainerScroll.bind(this); } setLoadingIndicatorDelay(loadingIndicatorDelay: number) { this.loadingIndicatorDelay = loadingIndicatorDelay; } protected override _ensure$Container() { if (typeof this.options$Container === 'function') { // resolve function provided by options.$container that returns a jQuery element this.$container = this.options$Container(); } else if (this.options$Container) { // use jQuery element provided by options.$container this.$container = this.options$Container; } else { // default: when no options.$container is not set, use jQuery element of widget this.$container = this.widget.$container; } } /** * @param immediate whether the {@link loadingIndicatorDelay} should be ignored and the indicator rendered immediately. */ renderLoading(immediate = false) { // Clear any pending loading function clearTimeout(this._loadingIndicatorTimeoutId); this._ensure$Container(); if (this.widget.isLoading()) { // add loading indicator if (!immediate && this.loadingIndicatorDelay && !this.widget.rendering) { this._loadingIndicatorTimeoutId = setTimeout( this._renderLoadingIndicator.bind(this), this.loadingIndicatorDelay); } else { this._renderLoadingIndicator(); } } else { // remove loading indicator this._removeLoadingIndicator(); } } protected _renderLoadingIndicator() { if (this._$loadingIndicator || !this.widget.rendered && !this.widget.rendering) { return; } // Hide widget content this.$container.addClass('loading'); if (this.withGlassPane) { this._glassPane = scout.create(GlassPane, { parent: this.widget, cssClass: 'loading-glasspane' }); this._glassPane.render(this.$container); if (this.$container.data('scrollable')) { // If $container is scrollable, glasspane needs to be moved into the viewport this._updateGlassPanePosition(); this.$container.on('scroll', this._containerScrollHandler); } } // Create loading indicator let $indicatorParent = this._glassPane ? this._glassPane.$container : this.$container; this._$loadingIndicator = $indicatorParent.appendDiv('loading-indicator'); } protected _removeLoadingIndicator() { if (!this._$loadingIndicator) { return; } this._glassPane?.deactivate(); // Ensure glasspane does not prevent focusing an element underneath while loading indicator is being removed. this._$loadingIndicator.css('opacity', this._$loadingIndicator.css('opacity')); this._$loadingIndicator.addClass('animate-remove'); this._$loadingIndicator.oneAnimationEnd(() => { this.remove(); if (this.widget.rendered) { // Show widget's content (layout if necessary) this.$container.removeClass('loading'); this.widget.invalidateLayoutTree(); } }); } remove() { if (this._$loadingIndicator) { this._$loadingIndicator.remove(); this._$loadingIndicator = null; } this.$container?.off('scroll', this._containerScrollHandler); this._glassPane?.destroy(); } protected _onContainerScroll(event: JQuery.ScrollEvent) { this._updateGlassPanePosition(); } protected _updateGlassPanePosition() { if (!this._glassPane?.rendered || !this.$container) { return; } this._glassPane.$container .cssTop(this.$container.scrollTop()) .cssLeft(this.$container.scrollLeft()); } }