export interface CountryIdentity { code: string; name: string; } export interface CountryHoverCache { code: string | null; name: string | null; } export type CancelableFrameTask = (() => void) & { cancel(): void }; export interface CountryHoverQueryController { queue(point: TPoint): void; cancel(): void; isPending(): boolean; } export function shouldRunInputSensitiveMapWork(isInputPending: () => boolean): boolean { return !isInputPending(); } export function shouldRenderTradeAnimationFrame(frameCount: number, isInputPending: () => boolean): boolean { return frameCount % 2 === 0 && shouldRunInputSensitiveMapWork(isInputPending); } export function resolveCountryForPointerInteraction( hover: CountryHoverCache, hoverQueryPending: boolean, resolveFromCoordinate: () => CountryIdentity | null, ): CountryIdentity | null { if (!hoverQueryPending && hover.code && hover.name) { return { code: hover.code, name: hover.name }; } return resolveFromCoordinate(); } export function createCountryHoverQueryController( scheduleFrame: (run: () => void) => CancelableFrameTask, runQuery: (point: TPoint) => void, ): CountryHoverQueryController { let pendingPoint: TPoint | null = null; let pending = false; const frameTask = scheduleFrame(() => { const point = pendingPoint; pendingPoint = null; pending = false; if (point !== null) runQuery(point); }); return { queue(point: TPoint): void { pendingPoint = point; pending = true; frameTask(); }, cancel(): void { pendingPoint = null; pending = false; frameTask.cancel(); }, isPending(): boolean { return pending; }, }; }