/
vshmidt
/
label-studio
Обзор
Документация
Войти
/
vshmidt
/
label-studio
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
develop
web/libs/editor/src/components/KonvaVector/VectorSelectionTracker.ts
180 строк
6 KB
Nick Skriabin
fix: BROS-464: Vector region selection (#8636)
03 ноя 2025, 20:28
Не верифицирован
03 ноя 2025, 20:28
0c9b9eb
Код
Авторство
О чём код?
import type { BezierPoint } from "./types"; export interface GlobalSelectionState { selectedInstances: Map<string, Set<number>>; // instanceId -> selected point indices activeInstanceId: string | null; isTransforming: boolean; transformerState: { rotation: number; scaleX: number; scaleY: number; centerX: number; centerY: number; } | null; } export interface VectorInstance { id: string; getPoints: () => BezierPoint[]; updatePoints: (points: BezierPoint[]) => void; setSelectedPoints: (selectedPoints: Set<number>) => void; setSelectedPointIndex: (index: number | null) => void; onPointSelected?: (index: number | null) => void; onTransformationComplete?: (data: any) => void; getTransform: () => { zoom: number; offsetX: number; offsetY: number }; getFitScale: () => number; getBounds: () => { width: number; height: number } | undefined; } export class VectorSelectionTracker { private static instance: VectorSelectionTracker | null = null; private state: GlobalSelectionState = { selectedInstances: new Map(), activeInstanceId: null, isTransforming: false, transformerState: null, }; private instances: Map<string, VectorInstance> = new Map(); private listeners: Set<(state: GlobalSelectionState) => void> = new Set(); private constructor() {} static getInstance(): VectorSelectionTracker { if (!VectorSelectionTracker.instance) { VectorSelectionTracker.instance = new VectorSelectionTracker(); } return VectorSelectionTracker.instance; } // Instance Management registerInstance(instance: VectorInstance): void { this.instances.set(instance.id, instance); } unregisterInstance(instanceId: string): void { this.instances.delete(instanceId); this.state.selectedInstances.delete(instanceId); // If this was the active instance, clear it if (this.state.activeInstanceId === instanceId) { this.state.activeInstanceId = null; } this.notifyListeners(); } // Selection Management selectPoints(instanceId: string, pointIndices: Set<number>): void { // Allow multiple instances to have selections simultaneously // Removed the blocking logic that prevented multiple vector regions from being selected if (pointIndices.size === 0) { this.state.selectedInstances.delete(instanceId); // If this was the active instance and we're clearing selection, clear active instance if (this.state.activeInstanceId === instanceId) { this.state.activeInstanceId = null; } } else { this.state.selectedInstances.set(instanceId, new Set(pointIndices)); // Set this as the active instance for transformation purposes // Multiple instances can now have selections, but only one can be actively transforming this.state.activeInstanceId = instanceId; } this.notifyListeners(); // Update the instance's local selection state const instance = this.instances.get(instanceId); if (instance) { instance.setSelectedPoints(pointIndices); instance.setSelectedPointIndex(pointIndices.size === 1 ? Array.from(pointIndices)[0] : null); instance.onPointSelected?.(pointIndices.size === 1 ? Array.from(pointIndices)[0] : null); } } // Check if an instance can have selection canInstanceHaveSelection(instanceId: string): boolean { // Allow all instances to have selections simultaneously return true; } // Get the currently active instance ID getActiveInstanceId(): string | null { return this.state.activeInstanceId; } clearSelection(): void { // Clear all instance selections for (const [instanceId, instance] of this.instances) { instance.setSelectedPoints(new Set()); instance.setSelectedPointIndex(null); instance.onPointSelected?.(null); } this.state.selectedInstances.clear(); this.state.activeInstanceId = null; this.notifyListeners(); } getGlobalSelection(): GlobalSelectionState { return { ...this.state }; } getSelectedPoints(): Array<{ instanceId: string; pointIndex: number; point: BezierPoint }> { const selectedPoints: Array<{ instanceId: string; pointIndex: number; point: BezierPoint }> = []; for (const [instanceId, pointIndices] of this.state.selectedInstances) { const instance = this.instances.get(instanceId); if (instance) { const points = instance.getPoints(); for (const pointIndex of pointIndices) { if (pointIndex < points.length) { selectedPoints.push({ instanceId, pointIndex, point: points[pointIndex], }); } } } } return selectedPoints; } // Event Listeners subscribe(listener: (state: GlobalSelectionState) => void): () => void { this.listeners.add(listener); return () => { this.listeners.delete(listener); }; } private notifyListeners(): void { const globalState = this.getGlobalSelection(); for (const listener of this.listeners) { listener(globalState); } } // Utility Methods hasSelection(): boolean { return this.state.selectedInstances.size > 0; } getSelectionCount(): number { let count = 0; for (const pointIndices of this.state.selectedInstances.values()) { count += pointIndices.size; } return count; } isInstanceSelected(instanceId: string): boolean { return this.state.selectedInstances.has(instanceId); } getInstanceSelection(instanceId: string): Set<number> | undefined { return this.state.selectedInstances.get(instanceId); } }