"use client" import { ActionIcon, Box, Collapse, Flex, Group, Paper, ScrollArea, Stack, Switch, Text, Title, Tooltip, } from "@mantine/core" import { ChevronDown, ChevronUp, Link2Off, Pencil, Trash2 } from "lucide-react" import { useCallback, useState } from "react" import { Project } from "../api/project/typing" import { LabelState } from "../LabelNossr" import { useKeyEventStore, useLabelStore, useObjectStore } from "../store" import { useBottomToolsStore } from "../useBottomToolsStore" import { useOtherToolsStore } from "../useOtherToolsStore" import { getShowContextMenuPosition, usePaperStore } from "../usePaperStore" import { renderGroupPath } from "../useRenderGroupPath" import { useRightToolsStore } from "../useRightToolsStore" import { useTopToolsStore } from "../useTopToolsStore" import { labelTypeMap } from "../utils/constants" import RightGroupEditModal from "./RightGroupEditModal" interface RightGroupToolsComponentProps { labelState: LabelState projectDetail?: Project.DetailResponse } const RightGroupTools: React.FC = (props) => { const { projectDetail } = props const [open, setOpen] = useState(false) const [editId, setEditId] = useState(null) const getOperationSchema = useCallback( (operationId: string) => { return ( projectDetail?.label_schema_list?.find( (item) => item.category_id === +operationId ) || null ) }, [projectDetail?.label_schema_list] ) const { setLabel, pushStateStack } = useLabelStore() const { pathGroupMap, groupPathVisible, setGroupPathVisible } = useRightToolsStore() const { activeImage } = useBottomToolsStore() const { groupStatus, updateGroupStatus, selectedPath, updateSelectedPath } = useObjectStore() const { getItemById, getItemsById } = usePaperStore() const { shift: pressShift } = useKeyEventStore() const [isGroupCollapsed, setIsGroupCollapsed] = useState(true) const handleSelect = useCallback( (id: number) => { let operationId = getItemById(id)?.data.operationId if (selectedPath[activeImage]?.length) { if (!selectedPath[activeImage]?.includes(id)) { if (pressShift) { if (getItemsById(id)?.length) { getItemsById(id).forEach((item) => { item.fillColor = item.data?.fillColor }) let category = useTopToolsStore .getState() .objectOperations.find( (item) => item.category_id.toString() === operationId ) const globalPosition = getItemById(id)!.globalMatrix.transform( getItemById(id)!.position ) const { top, left } = getShowContextMenuPosition( globalPosition, category! ) useOtherToolsStore.getState().setShowContextMenu({ showContextMenu: true, position: { top, left }, imageId: activeImage, operationId: operationId, id, }) } } else { selectedPath[activeImage].forEach((selectedId) => { if (getItemsById(selectedId)?.length) { getItemsById(selectedId).forEach((item) => { item.fillColor = item.data?.blankColor }) useOtherToolsStore.getState().setShowContextMenu({ showContextMenu: false, position: { top: 0, left: 0, }, imageId: activeImage, operationId: operationId, id, }) } }) if (getItemsById(id)?.length) { getItemsById(id).forEach((item) => { item.fillColor = item.data?.fillColor }) let category = useTopToolsStore .getState() .objectOperations.find( (item) => item.category_id.toString() === operationId ) const globalPosition = getItemById(id)!.globalMatrix.transform( getItemById(id)!.position ) const { top, left } = getShowContextMenuPosition( globalPosition, category! ) useOtherToolsStore.getState().setShowContextMenu({ showContextMenu: true, position: { top, left }, imageId: activeImage, operationId: operationId, id, }) } } updateSelectedPath(activeImage, "ADD", id) } else { updateSelectedPath(activeImage, "DELETE", id) if (getItemsById(id)?.length) { getItemsById(id).forEach((item) => { item.fillColor = item.data?.blankColor }) useOtherToolsStore.getState().setShowContextMenu({ showContextMenu: false, position: { top: 0, left: 0, }, imageId: activeImage, operationId: operationId, id, }) } } } else { updateSelectedPath(activeImage, "ADD", id) if (getItemsById(id)?.length) { getItemsById(id).forEach((item) => { item.fillColor = item.data?.fillColor }) let category = useTopToolsStore .getState() .objectOperations.find( (item) => item.category_id.toString() === operationId ) const globalPosition = getItemById(id)!.globalMatrix.transform( getItemById(id)!.position ) const { top, left } = getShowContextMenuPosition( globalPosition, category! ) useOtherToolsStore.getState().setShowContextMenu({ showContextMenu: true, position: { top, left }, imageId: activeImage, operationId: operationId, id, }) } } }, [ activeImage, getItemById, getItemsById, pressShift, selectedPath, updateSelectedPath, ] ) const getOperationIdByPathId = (pathId: number) => { let pathItem = getItemById(pathId) return pathItem?.data?.operationId || "" } const unlinkGroup = (id: number) => { const group = usePaperStore.getState().group const imageId = useBottomToolsStore.getState().activeImage const groupMap = useRightToolsStore.getState().pathGroupMap.get(imageId) ?? new Map() if (!group) return if (!Array.from(groupMap.values()).length) return // update children path data const currentGroupChildrenIds = groupMap.get(id) ?? [] const needUpdateOperationIds: any[] = [] currentGroupChildrenIds?.forEach((childId, index) => { const childPath = usePaperStore.getState().getItemById(childId)! childPath.data.parentGroupId = null if (childPath.data.operationId && !needUpdateOperationIds[index]) needUpdateOperationIds[index] = childPath.data.operationId }) // handle labeldata update const currentData = structuredClone(useLabelStore.getState().label) needUpdateOperationIds.forEach((operationId, index) => { if (currentData.get(imageId)?.get(operationId)?.length) { let result = currentData .get(imageId) ?.get(operationId) ?.map((item) => item[0] !== currentGroupChildrenIds[index] ? item : [ item[0], item[1], { ...item[2], parentGroupId: null }, item[3], ] ) || [] currentData.get(imageId)?.set(operationId, result as any) } }) // update group tool useRightToolsStore.getState().removePathGroupInMap(imageId, id) // setlabeldata setLabel(currentData) pushStateStack(currentData) } const deleteGroup = (id: number) => { const group = usePaperStore.getState().group const imageId = useBottomToolsStore.getState().activeImage const groupMap = useRightToolsStore.getState().pathGroupMap.get(imageId) ?? new Map() if (!group) return if (!Array.from(groupMap.values()).length) return // remove group children const currentGroupChildrenIds = groupMap.get(id) ?? [] const needRemoveOperationIds: any[] = [] currentGroupChildrenIds?.forEach((childId, index) => { const childPaths = group.getItems({ data: { id: childId } }) childPaths.forEach((p) => { p.remove() if (p.data.operationId && !needRemoveOperationIds[index]) needRemoveOperationIds[index] = p.data.operationId }) }) const currentData = structuredClone(useLabelStore.getState().label) needRemoveOperationIds.forEach((operationId, index) => { if (currentData.get(imageId)?.get(operationId)?.length) { let result = currentData .get(imageId) ?.get(operationId) ?.filter((item) => item[0] !== currentGroupChildrenIds[index]) || [] currentData.get(imageId)?.set(operationId, result) } }) // update group tool useRightToolsStore.getState().removePathGroupInMap(imageId, id) setLabel(currentData) pushStateStack(currentData) } return ( 群组列表 setIsGroupCollapsed(!isGroupCollapsed)}> {isGroupCollapsed ? ( ) : ( )} 展示群组框 { setGroupPathVisible(event.currentTarget.checked) renderGroupPath() }} /> {pathGroupMap.get(activeImage) && Array.from(pathGroupMap.get(activeImage)!.keys())?.map( (groupId) => { const isOpen = !groupStatus[activeImage + groupId]?.["COLLAPSE"] return ( @{groupId} { setOpen(true) setEditId(groupId) }}> { unlinkGroup(groupId) }}> { deleteGroup(groupId) }}> {pathGroupMap.get(activeImage)?.get(groupId) ?.length || 0} { updateGroupStatus( activeImage + groupId, "COLLAPSE", isOpen ) }}> {isOpen ? ( ) : ( )} {pathGroupMap .get(activeImage) ?.get(groupId) ?.map((child) => { const isSelected = selectedPath[activeImage]?.includes(child) return ( { handleSelect(child) }} style={{ cursor: "pointer" }}> {child}- {labelTypeMap.get( getOperationSchema( getOperationIdByPathId(child) )?.label_type || 0 )} ) })} ) } )} {open && ( { setEditId(val) }} closeModal={() => { setOpen(false) setEditId(null) }} /> )} ) } export default RightGroupTools