Portal/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/NodeContextMenu.tsx
2024-07-29 22:30:24 +03:00

167 lines
5.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use client';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { IconConnect, IconDestroy, IconEdit2, IconExecute, IconNewItem, IconRSForm } from '@/components/Icons';
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;
onEditSchema: (target: OperationID) => void;
onEditOperation: (target: OperationID) => void;
onRunOperation: (target: OperationID) => void;
}
function NodeContextMenu({
operation,
cursorX,
cursorY,
onHide,
onDelete,
onCreateInput,
onEditSchema,
onEditOperation,
onRunOperation
}: NodeContextMenuProps) {
const controller = useOssEdit();
const [isOpen, setIsOpen] = useState(false);
const ref = useRef(null);
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]);
const handleHide = useCallback(() => {
setIsOpen(false);
onHide();
}, [onHide]);
useClickedOutside({ ref, callback: handleHide });
useEffect(() => setIsOpen(true), []);
const handleOpenSchema = () => {
controller.openOperationSchema(operation.id);
};
const handleEditSchema = () => {
handleHide();
onEditSchema(operation.id);
};
const handleEditOperation = () => {
handleHide();
onEditOperation(operation.id);
};
const handleDeleteOperation = () => {
handleHide();
onDelete(operation.id);
};
const handleCreateSchema = () => {
handleHide();
onCreateInput(operation.id);
};
const handleRunSynthesis = () => {
handleHide();
onRunOperation(operation.id);
};
return (
<div ref={ref} className='absolute' style={{ top: cursorY, left: cursorX, width: 10, height: 10 }}>
<Dropdown isOpen={isOpen} stretchLeft={cursorX >= window.innerWidth - PARAMETER.ossContextMenuWidth}>
<DropdownButton
text='Редактировать'
titleHtml={prepareTooltip('Редактировать операцию', 'Ctrl + клик')}
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='Создать пустую схему для загрузки'
icon={<IconNewItem size='1rem' className='icon-green' />}
disabled={controller.isProcessing}
onClick={handleCreateSchema}
/>
) : null}
{controller.isMutable && operation.operation_type === OperationType.INPUT ? (
<DropdownButton
text={!operation.result ? 'Загрузить схему' : 'Изменить схему'}
title='Выбрать схему для загрузки'
icon={<IconConnect size='1rem' className='icon-primary' />}
disabled={controller.isProcessing}
onClick={handleEditSchema}
/>
) : null}
{controller.isMutable && !operation.result && operation.operation_type === OperationType.SYNTHESIS ? (
<DropdownButton
text='Выполнить синтез'
title={
readyForSynthesis
? 'Выполнить операцию и получить синтезированную КС'
: 'Необходимо предоставить все аргументы'
}
icon={<IconExecute size='1rem' className='icon-green' />}
disabled={controller.isProcessing || !readyForSynthesis}
onClick={handleRunSynthesis}
/>
) : null}
<DropdownButton
text='Удалить операцию'
icon={<IconDestroy size='1rem' className='icon-red' />}
disabled={!controller.isMutable || controller.isProcessing}
onClick={handleDeleteOperation}
/>
</Dropdown>
</div>
);
}
export default NodeContextMenu;