You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

172 lines
4.1 KiB

import { create } from "zustand";
import { AnnotationShape, ToolType, ToolOptions } from "@/types";
interface AnnotationStore {
// Modal state
isModalOpen: boolean;
sourceNodeId: string | null;
sourceImage: string | null;
// Annotations
annotations: AnnotationShape[];
selectedShapeId: string | null;
// History for undo/redo
history: AnnotationShape[][];
historyIndex: number;
// Current tool and options
currentTool: ToolType;
toolOptions: ToolOptions;
// Modal actions
openModal: (nodeId: string, image: string, existingAnnotations?: AnnotationShape[]) => void;
closeModal: () => void;
// Annotation actions
addAnnotation: (shape: AnnotationShape) => void;
updateAnnotation: (id: string, updates: Partial<AnnotationShape>) => void;
deleteAnnotation: (id: string) => void;
clearAnnotations: () => void;
selectShape: (id: string | null) => void;
// History actions
undo: () => void;
redo: () => void;
pushHistory: () => void;
// Tool actions
setCurrentTool: (tool: ToolType) => void;
setToolOptions: (options: Partial<ToolOptions>) => void;
}
const defaultToolOptions: ToolOptions = {
strokeColor: "#ef4444", // red-500
strokeWidth: 3,
fillColor: null,
fontSize: 24,
opacity: 1,
};
export const useAnnotationStore = create<AnnotationStore>((set, get) => ({
isModalOpen: false,
sourceNodeId: null,
sourceImage: null,
annotations: [],
selectedShapeId: null,
history: [[]],
historyIndex: 0,
currentTool: "rectangle",
toolOptions: defaultToolOptions,
openModal: (nodeId: string, image: string, existingAnnotations: AnnotationShape[] = []) => {
set({
isModalOpen: true,
sourceNodeId: nodeId,
sourceImage: image,
annotations: existingAnnotations,
selectedShapeId: null,
history: [existingAnnotations],
historyIndex: 0,
});
},
closeModal: () => {
set({
isModalOpen: false,
sourceNodeId: null,
sourceImage: null,
annotations: [],
selectedShapeId: null,
history: [[]],
historyIndex: 0,
});
},
addAnnotation: (shape: AnnotationShape) => {
const { pushHistory } = get();
pushHistory();
set((state) => ({
annotations: [...state.annotations, shape],
}));
},
updateAnnotation: (id: string, updates: Partial<AnnotationShape>) => {
set((state) => ({
annotations: state.annotations.map((shape) =>
shape.id === id ? { ...shape, ...updates } as AnnotationShape : shape
),
}));
},
deleteAnnotation: (id: string) => {
const { pushHistory } = get();
pushHistory();
set((state) => ({
annotations: state.annotations.filter((shape) => shape.id !== id),
selectedShapeId: state.selectedShapeId === id ? null : state.selectedShapeId,
}));
},
clearAnnotations: () => {
const { pushHistory } = get();
pushHistory();
set({
annotations: [],
selectedShapeId: null,
});
},
selectShape: (id: string | null) => {
set({ selectedShapeId: id });
},
pushHistory: () => {
set((state) => {
const newHistory = state.history.slice(0, state.historyIndex + 1);
newHistory.push([...state.annotations]);
return {
history: newHistory,
historyIndex: newHistory.length - 1,
};
});
},
undo: () => {
set((state) => {
if (state.historyIndex > 0) {
const newIndex = state.historyIndex - 1;
return {
historyIndex: newIndex,
annotations: [...state.history[newIndex]],
selectedShapeId: null,
};
}
return state;
});
},
redo: () => {
set((state) => {
if (state.historyIndex < state.history.length - 1) {
const newIndex = state.historyIndex + 1;
return {
historyIndex: newIndex,
annotations: [...state.history[newIndex]],
selectedShapeId: null,
};
}
return state;
});
},
setCurrentTool: (tool: ToolType) => {
set({ currentTool: tool, selectedShapeId: null });
},
setToolOptions: (options: Partial<ToolOptions>) => {
set((state) => ({
toolOptions: { ...state.toolOptions, ...options },
}));
},
}));