From 3aa08e3d1782b62b31c52f58f1c3701b1e6d729f Mon Sep 17 00:00:00 2001 From: MrWindlike Date: Fri, 1 Sep 2023 16:43:08 +0800 Subject: [PATCH] feat(Editor): not allowed to remove component that is used by others --- .../RelationshipModal/RelationshipModal.tsx | 4 +- .../RelationshipModal/RelationshipView.tsx | 4 +- .../StructureTree/ComponentNode.tsx | 29 ++++++++--- packages/editor/src/hooks/useModal.tsx | 52 +++++++++++++++++++ 4 files changed, 79 insertions(+), 10 deletions(-) create mode 100644 packages/editor/src/hooks/useModal.tsx diff --git a/packages/editor/src/components/RelationshipModal/RelationshipModal.tsx b/packages/editor/src/components/RelationshipModal/RelationshipModal.tsx index 38da3721e..28c06944c 100644 --- a/packages/editor/src/components/RelationshipModal/RelationshipModal.tsx +++ b/packages/editor/src/components/RelationshipModal/RelationshipModal.tsx @@ -8,7 +8,7 @@ import { ModalBody, } from '@chakra-ui/react'; import { EditorServices } from '../../types'; -import { ReplationshipView } from './RelationshipView'; +import { RelationshipView } from './RelationshipView'; type Props = { componentId: string; @@ -28,7 +28,7 @@ export const RelationshipModal: React.FC = ({ Relationships of {componentId} - + diff --git a/packages/editor/src/components/RelationshipModal/RelationshipView.tsx b/packages/editor/src/components/RelationshipModal/RelationshipView.tsx index 83af37908..ab61430d0 100644 --- a/packages/editor/src/components/RelationshipModal/RelationshipView.tsx +++ b/packages/editor/src/components/RelationshipModal/RelationshipView.tsx @@ -33,7 +33,7 @@ type MethodRelation = { method: string; }; -export const ReplationshipView: React.FC = ({ componentId, services }) => { +export const RelationshipView: React.FC = ({ componentId, services }) => { const { appModelManager, editorStore } = services; const { appModel } = appModelManager; @@ -128,7 +128,7 @@ export const ReplationshipView: React.FC = ({ componentId, services }) => ); }; -function getRelations(componentId: ComponentId, appModel: AppModel) { +export function getRelations(componentId: ComponentId, appModel: AppModel) { const expressionRelations: ExpressionRelation[] = []; const methodRelations: MethodRelation[] = []; appModel.traverseTree(c => { diff --git a/packages/editor/src/components/StructureTree/ComponentNode.tsx b/packages/editor/src/components/StructureTree/ComponentNode.tsx index d6baa797e..9268c00d9 100644 --- a/packages/editor/src/components/StructureTree/ComponentNode.tsx +++ b/packages/editor/src/components/StructureTree/ComponentNode.tsx @@ -23,6 +23,8 @@ import { RelationshipModal } from '../RelationshipModal'; import { ExplorerMenuTabs } from '../../constants/enum'; import { ExtractModuleModal } from '../ExtractModuleModal'; import { copyToClipboard } from '../../utils/copy'; +import { getRelations } from '../RelationshipModal/RelationshipView'; +import useModal from '../../hooks/useModal'; const IndextWidth = 24; @@ -55,22 +57,36 @@ const ComponentNodeImpl = (props: Props) => { } = props; const toast = useToast(); const { registry, eventBus, appModelManager, editorStore, stateManager } = services; + const { appModel } = appModelManager; const [isShowRelationshipModal, setIsShowRelationshipModal] = useState(false); const [isShowExtractModuleModal, setIsShowExtractModuleModal] = useState(false); + const { modal: messageModal, open: openMessageModal } = useModal(); const slots = Object.keys(registry.getComponentByType(component.type).spec.slots); const paddingLeft = depth * IndextWidth; const onClickRemove = useCallback( (e: React.MouseEvent) => { e.stopPropagation(); - eventBus.send( - 'operation', - genOperation(registry, 'removeComponent', { - componentId: component.id, - }) + const { expressionRelations, methodRelations } = getRelations( + component.id as ComponentId, + appModel ); + + if (expressionRelations.length || methodRelations.length) { + openMessageModal({ + title: 'Remove component failed', + message: 'Its state or methods are used by another component.', + }); + } else { + eventBus.send( + 'operation', + genOperation(registry, 'removeComponent', { + componentId: component.id, + }) + ); + } }, - [component.id, eventBus, registry] + [component.id, eventBus, registry, appModel, openMessageModal] ); const onClickDuplicate = useCallback( (e: React.MouseEvent) => { @@ -307,6 +323,7 @@ const ComponentNodeImpl = (props: Props) => { {emptyChildrenSlotsPlaceholder} {relationshipViewModal} {extractModuleModal} + {messageModal} ); }; diff --git a/packages/editor/src/hooks/useModal.tsx b/packages/editor/src/hooks/useModal.tsx new file mode 100644 index 000000000..9b1337416 --- /dev/null +++ b/packages/editor/src/hooks/useModal.tsx @@ -0,0 +1,52 @@ +import React, { useCallback, useState } from 'react'; +import { + Modal, + ModalOverlay, + ModalContent, + ModalHeader, + ModalCloseButton, + ModalBody, + ModalFooter, + Button, + useDisclosure, +} from '@chakra-ui/react'; + +export type OpenOptions = { + title?: string; + message?: string; +}; + +function useModal() { + const { isOpen, onOpen, onClose } = useDisclosure(); + const [options, setOptions] = useState>({}); + const { title, message } = options; + const open = useCallback( + (options: OpenOptions) => { + setOptions(options); + onOpen(); + }, + [onOpen] + ); + + const modal = ( + + + + {title ? {title} : null} + + {message} + + + + + + ); + + return { + modal, + open, + close: onClose, + }; +} + +export default useModal;