ConceptPortal-public/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/NodeContextMenu.tsx

167 lines
5.4 KiB
TypeScript
Raw Normal View History

2024-07-26 17:31:57 +03:00
'use client';
2024-07-29 16:56:24 +03:00
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
2024-07-26 17:31:57 +03:00
2024-08-03 12:00:10 +03:00
import { IconConnect, IconDestroy, IconEdit2, IconExecute, IconNewRSForm, IconRSForm } from '@/components/Icons';
2024-07-26 17:31:57 +03:00
import Dropdown from '@/components/ui/Dropdown';
import DropdownButton from '@/components/ui/DropdownButton';
import useClickedOutside from '@/hooks/useClickedOutside';
import { IOperation, OperationID, OperationType } from '@/models/oss';
import { PARAMETER } from '@/utils/constants';
import { prepareTooltip } from '@/utils/labels';
import { useOssEdit } from '../OssEditContext';
export interface ContextMenuData {
operation: IOperation;
cursorX: number;
cursorY: number;
}
interface NodeContextMenuProps extends ContextMenuData {
onHide: () => void;
onDelete: (target: OperationID) => void;
onCreateInput: (target: OperationID) => void;
2024-07-28 21:30:10 +03:00
onEditSchema: (target: OperationID) => void;
2024-07-29 16:56:24 +03:00
onEditOperation: (target: OperationID) => void;
onExecuteOperation: (target: OperationID) => void;
2024-07-26 17:31:57 +03:00
}
2024-07-28 21:30:10 +03:00
function NodeContextMenu({
operation,
cursorX,
cursorY,
onHide,
onDelete,
onCreateInput,
2024-07-29 16:56:24 +03:00
onEditSchema,
2024-07-29 22:31:11 +03:00
onEditOperation,
onExecuteOperation
2024-07-28 21:30:10 +03:00
}: NodeContextMenuProps) {
2024-07-26 17:31:57 +03:00
const controller = useOssEdit();
const [isOpen, setIsOpen] = useState(false);
const ref = useRef(null);
2024-07-29 16:56:24 +03:00
const readyForSynthesis = useMemo(() => {
if (operation.operation_type !== OperationType.SYNTHESIS) {
return false;
}
if (!controller.schema || operation.result) {
return false;
}
const argumentIDs = controller.schema.graph.expandInputs([operation.id]);
if (!argumentIDs || argumentIDs.length < 2) {
return false;
}
const argumentOperations = argumentIDs.map(id => controller.schema!.operationByID.get(id)!);
if (argumentOperations.some(item => item.result === null)) {
return false;
}
return true;
}, [operation, controller.schema]);
2024-07-26 17:31:57 +03:00
const handleHide = useCallback(() => {
setIsOpen(false);
onHide();
}, [onHide]);
useClickedOutside({ ref, callback: handleHide });
useEffect(() => setIsOpen(true), []);
const handleOpenSchema = () => {
controller.openOperationSchema(operation.id);
};
const handleEditSchema = () => {
handleHide();
2024-07-28 21:30:10 +03:00
onEditSchema(operation.id);
2024-07-26 17:31:57 +03:00
};
const handleEditOperation = () => {
handleHide();
2024-07-29 16:56:24 +03:00
onEditOperation(operation.id);
2024-07-26 17:31:57 +03:00
};
const handleDeleteOperation = () => {
handleHide();
onDelete(operation.id);
};
const handleCreateSchema = () => {
handleHide();
onCreateInput(operation.id);
};
const handleRunSynthesis = () => {
handleHide();
onExecuteOperation(operation.id);
};
2024-07-26 17:31:57 +03:00
return (
<div ref={ref} className='absolute select-none' style={{ top: cursorY, left: cursorX, width: 10, height: 10 }}>
2024-07-26 17:31:57 +03:00
<Dropdown isOpen={isOpen} stretchLeft={cursorX >= window.innerWidth - PARAMETER.ossContextMenuWidth}>
<DropdownButton
text='Редактировать'
titleHtml={prepareTooltip('Редактировать операцию', 'Двойной клик')}
2024-07-26 17:31:57 +03:00
icon={<IconEdit2 size='1rem' className='icon-primary' />}
disabled={controller.isProcessing}
onClick={handleEditOperation}
/>
{operation.result ? (
<DropdownButton
text='Открыть схему'
title='Открыть привязанную КС'
icon={<IconRSForm size='1rem' className='icon-green' />}
disabled={controller.isProcessing}
onClick={handleOpenSchema}
/>
) : null}
{controller.isMutable && !operation.result && operation.operation_type === OperationType.INPUT ? (
<DropdownButton
text='Создать схему'
title='Создать пустую схему для загрузки'
2024-08-03 12:00:10 +03:00
icon={<IconNewRSForm size='1rem' className='icon-green' />}
2024-07-26 17:31:57 +03:00
disabled={controller.isProcessing}
onClick={handleCreateSchema}
2024-07-26 17:31:57 +03:00
/>
) : null}
2024-07-28 21:30:10 +03:00
{controller.isMutable && operation.operation_type === OperationType.INPUT ? (
2024-07-26 17:31:57 +03:00
<DropdownButton
2024-07-28 21:30:10 +03:00
text={!operation.result ? 'Загрузить схему' : 'Изменить схему'}
2024-07-26 17:31:57 +03:00
title='Выбрать схему для загрузки'
icon={<IconConnect size='1rem' className='icon-primary' />}
2024-07-26 17:31:57 +03:00
disabled={controller.isProcessing}
onClick={handleEditSchema}
/>
) : null}
{controller.isMutable && !operation.result && operation.operation_type === OperationType.SYNTHESIS ? (
<DropdownButton
text='Выполнить синтез'
2024-07-29 16:56:24 +03:00
title={
readyForSynthesis
? 'Выполнить операцию и получить синтезированную КС'
: 'Необходимо предоставить все аргументы'
}
icon={<IconExecute size='1rem' className='icon-green' />}
2024-07-29 16:56:24 +03:00
disabled={controller.isProcessing || !readyForSynthesis}
onClick={handleRunSynthesis}
/>
) : null}
2024-07-26 17:31:57 +03:00
<DropdownButton
text='Удалить операцию'
icon={<IconDestroy size='1rem' className='icon-red' />}
disabled={!controller.isMutable || controller.isProcessing}
onClick={handleDeleteOperation}
/>
</Dropdown>
</div>
);
}
export default NodeContextMenu;