@eclipse-scout/core
Version:
Eclipse Scout runtime
137 lines (121 loc) • 4.64 kB
text/typescript
/*
* 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());
}
}