import { LitElement } from 'lit'; import { type Constructor } from './mixin'; const DEFAULT_KEY = '_default_'; export const PROCESSING_START_EVENT_NAME = 'processing-start'; export const PROCESSING_STOP_EVENT_NAME = 'processing-stop'; export interface ProcessingEvent { key?: string; } type ProcessingFunction = () => void; type ProcessingAsyncFunction = () => Promise; interface ProcessingInterface { isProcessing(key?: string): boolean; startProcessing(key?: string): void; stopProcessing(key?: string): void; wrapProcessing(processFn: ProcessingFunction, key?: string): void; wrapAsyncProcessing( processFn: ProcessingAsyncFunction, key?: string, ): Promise; } /** * Mixin for being able to start and stop processing flags as an element state. */ export const ProcessingMixin = >( superClass: T, ) => { class ProcessingElement extends superClass { static styles = [(superClass as unknown as typeof LitElement).styles ?? []]; private processingInfo: Record = {}; isProcessing(key = DEFAULT_KEY): boolean { return this.processingInfo[key] === true; } startProcessing(key = DEFAULT_KEY) { this.processingInfo[key] = true; this.dispatchEvent( new CustomEvent(PROCESSING_START_EVENT_NAME, { bubbles: true, composed: true, detail: { key }, }), ); this.requestUpdate(); } stopProcessing(key = DEFAULT_KEY) { this.processingInfo[key] = false; this.dispatchEvent( new CustomEvent(PROCESSING_STOP_EVENT_NAME, { bubbles: true, composed: true, detail: { key }, }), ); this.requestUpdate(); } wrapProcessing(processFn: ProcessingFunction, key = DEFAULT_KEY) { try { this.startProcessing(key); processFn(); } finally { this.stopProcessing(key); } } async wrapAsyncProcessing( processFn: ProcessingAsyncFunction, key = DEFAULT_KEY, ): Promise { try { this.startProcessing(key); await processFn(); } finally { this.stopProcessing(key); } } } return ProcessingElement as Constructor & T; }; declare global { interface GlobalEventHandlersEventMap { processingStart: CustomEvent; processingStop: CustomEvent; } }