import { clampMaterialsLayout, DEFAULT_MATERIALS_OVERLAY_LAYOUT, type MaterialId, type MaterialsOverlayEvent, type MaterialsOverlayLayout, type MaterialsOverlayState, type MaterialsZoomTool, zoomMaterialsLayoutAt, } from '../../shared/types'; function emptyState(): MaterialsOverlayState { return { revision: 1, activeMaterialId: null, layout: { ...DEFAULT_MATERIALS_OVERLAY_LAYOUT }, legendLayout: { ...DEFAULT_MATERIALS_OVERLAY_LAYOUT, cx: 0.82, cy: 0.5, scale: 0.85 }, zoomTool: null, }; } function initialLayout(rotationDeg?: number): MaterialsOverlayLayout { return clampMaterialsLayout({ ...DEFAULT_MATERIALS_OVERLAY_LAYOUT, rotationDeg: rotationDeg ?? 0, }); } function initialLegendLayout(): MaterialsOverlayLayout { return clampMaterialsLayout({ ...DEFAULT_MATERIALS_OVERLAY_LAYOUT, cx: 0.82, cy: 0.5, scale: 0.85, rotationDeg: 0, }); } export class MaterialsOverlayStore { private state: MaterialsOverlayState = emptyState(); getState(): MaterialsOverlayState { return this.state; } clear(): MaterialsOverlayState { if (this.state.activeMaterialId === null && this.state.zoomTool === null) { return this.state; } this.state = { revision: this.state.revision + 1, activeMaterialId: null, layout: { ...DEFAULT_MATERIALS_OVERLAY_LAYOUT }, legendLayout: initialLegendLayout(), zoomTool: null, }; return this.state; } dispatch(event: MaterialsOverlayEvent): MaterialsOverlayState { switch (event.kind) { case 'hide': return this.clear(); case 'show': this.state = { revision: this.state.revision + 1, activeMaterialId: event.materialId, layout: initialLayout(event.rotationDeg), legendLayout: initialLegendLayout(), zoomTool: this.state.zoomTool, }; return this.state; case 'toggle': { if (this.state.activeMaterialId === event.materialId) { return this.clear(); } this.state = { revision: this.state.revision + 1, activeMaterialId: event.materialId, layout: initialLayout(event.rotationDeg), legendLayout: initialLegendLayout(), zoomTool: this.state.zoomTool, }; return this.state; } case 'layout.set': { if (this.state.activeMaterialId === null) return this.state; this.state = { ...this.state, revision: this.state.revision + 1, layout: clampMaterialsLayout({ ...this.state.layout, ...event.layout, }), }; return this.state; } case 'legendLayout.set': { if (this.state.activeMaterialId === null) return this.state; this.state = { ...this.state, revision: this.state.revision + 1, legendLayout: clampMaterialsLayout({ ...this.state.legendLayout, ...event.layout, rotationDeg: 0, }), }; return this.state; } case 'zoomTool.set': { const tool: MaterialsZoomTool = event.tool; this.state = { ...this.state, revision: this.state.revision + 1, zoomTool: tool, }; return this.state; } case 'zoomAt': { if (this.state.activeMaterialId === null || !this.state.zoomTool) return this.state; const factor = this.state.zoomTool === 'zoomIn' ? 1.25 : 1 / 1.25; const layout: MaterialsOverlayLayout = zoomMaterialsLayoutAt( this.state.layout, event.nx, event.ny, factor, ); this.state = { ...this.state, revision: this.state.revision + 1, layout, }; return this.state; } default: return this.state; } } ensureMaterialStillExists(materialIds: ReadonlySet): MaterialsOverlayState { const active = this.state.activeMaterialId; if (active === null || materialIds.has(active)) return this.state; return this.clear(); } }