// deck.gl // SPDX-License-Identifier: MIT // Copyright (c) vis.gl contributors import type {WidgetPlacement, Layer, WidgetProps} from '@deck.gl/core'; import {render} from 'preact'; import {Widget} from '@deck.gl/core'; import {IconButton} from './lib/components/icon-button'; /** Properties for the LoadingWidget */ export type LoadingWidgetProps = WidgetProps & { /** Widget positioning within the view. Default 'top-left'. */ placement?: WidgetPlacement; /** View to attach to and interact with. Required when using multiple views */ viewId?: string | null; /** Tooltip message when loading */ label?: string; /** * Callback when the loading state changes. * Called when layers transition between loading and loaded states. */ onLoadingChange?: (loading: boolean) => void; }; /** * A non-interactive widget that shows a loading spinner if any layers are loading data */ export class LoadingWidget extends Widget { static defaultProps: Required = { ...Widget.defaultProps, id: 'loading', placement: 'top-left', viewId: null, label: 'Loading layer data', onLoadingChange: () => {} }; className = 'deck-widget-loading'; placement: WidgetPlacement = 'top-left'; loading = true; constructor(props: LoadingWidgetProps = {}) { super(props); this.setProps(this.props); } setProps(props: Partial) { this.placement = props.placement ?? this.placement; this.viewId = props.viewId ?? this.viewId; super.setProps(props); } onRenderHTML(rootElement: HTMLElement): void { render( // TODO(ibgreen) - this should not be a button, but styling is so nested that it is easier to reuse this component. this.loading && ( ), rootElement ); } onRedraw({layers}: {layers: Layer[]}): void { const loading = layers.some(layer => !layer.isLoaded); if (loading !== this.loading) { this.loading = loading; // Call callback when loading state changes this.props.onLoadingChange?.(loading); this.updateHTML(); } } // TODO(ibgreen) - this should not be a button, see above. handleClick() {} }