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

199 lines
6.1 KiB
TypeScript
Raw Normal View History

2024-07-26 17:31:57 +03:00
'use client';
2025-02-05 01:27:52 +03:00
import { useEffect, useRef, useState } from 'react';
2024-07-26 17:31:57 +03:00
import { useMutatingOss } from '@/backend/oss/useMutatingOss';
import {
IconChild,
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;
onRelocateConstituents: (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,
onRelocateConstituents
2024-07-28 21:30:10 +03:00
}: NodeContextMenuProps) {
2024-07-26 17:31:57 +03:00
const controller = useOssEdit();
const isProcessing = useMutatingOss();
2024-07-26 17:31:57 +03:00
const [isOpen, setIsOpen] = useState(false);
const ref = useRef<HTMLDivElement>(null);
const readyForSynthesis = (() => {
2024-07-29 16:56:24 +03:00
if (operation.operation_type !== OperationType.SYNTHESIS) {
return false;
}
if (operation.result) {
2024-07-29 16:56:24 +03:00
return false;
}
const argumentIDs = controller.schema.graph.expandInputs([operation.id]);
2024-09-16 19:38:51 +03:00
if (!argumentIDs || argumentIDs.length < 1) {
2024-07-29 16:56:24 +03:00
return false;
}
const argumentOperations = argumentIDs.map(id => controller.schema.operationByID.get(id)!);
2024-07-29 16:56:24 +03:00
if (argumentOperations.some(item => item.result === null)) {
return false;
}
return true;
})();
2024-07-26 17:31:57 +03:00
2025-02-05 01:27:52 +03:00
function handleHide() {
2024-07-26 17:31:57 +03:00
setIsOpen(false);
onHide();
2025-02-05 01:27:52 +03:00
}
2024-07-26 17:31:57 +03:00
useClickedOutside(isOpen, ref, handleHide);
2024-07-26 17:31:57 +03:00
useEffect(() => setIsOpen(true), []);
2025-02-05 01:27:52 +03:00
function handleOpenSchema() {
controller.navigateOperationSchema(operation.id);
2025-02-05 01:27:52 +03:00
}
2024-07-26 17:31:57 +03:00
2025-02-05 01:27:52 +03:00
function handleEditSchema() {
2024-07-26 17:31:57 +03:00
handleHide();
2024-07-28 21:30:10 +03:00
onEditSchema(operation.id);
2025-02-05 01:27:52 +03:00
}
2024-07-26 17:31:57 +03:00
2025-02-05 01:27:52 +03:00
function handleEditOperation() {
2024-07-26 17:31:57 +03:00
handleHide();
2024-07-29 16:56:24 +03:00
onEditOperation(operation.id);
2025-02-05 01:27:52 +03:00
}
2024-07-26 17:31:57 +03:00
2025-02-05 01:27:52 +03:00
function handleDeleteOperation() {
2024-07-26 17:31:57 +03:00
handleHide();
onDelete(operation.id);
2025-02-05 01:27:52 +03:00
}
2024-07-26 17:31:57 +03:00
2025-02-05 01:27:52 +03:00
function handleCreateSchema() {
handleHide();
onCreateInput(operation.id);
2025-02-05 01:27:52 +03:00
}
2025-02-05 01:27:52 +03:00
function handleRunSynthesis() {
handleHide();
onExecuteOperation(operation.id);
2025-02-05 01:27:52 +03:00
}
2025-02-05 01:27:52 +03:00
function handleRelocateConstituents() {
handleHide();
onRelocateConstituents(operation.id);
2025-02-05 01:27:52 +03:00
}
2024-07-26 17:31:57 +03:00
return (
2024-09-01 13:52:32 +03:00
<div ref={ref} className='absolute select-none' style={{ top: cursorY, left: cursorX }}>
<Dropdown
isOpen={isOpen}
stretchLeft={cursorX >= window.innerWidth - PARAMETER.ossContextMenuWidth}
stretchTop={cursorY >= window.innerHeight - PARAMETER.ossContextMenuHeight}
>
2024-07-26 17:31:57 +03:00
<DropdownButton
text='Редактировать'
2024-08-17 12:17:13 +03:00
title='Редактировать операцию'
2024-07-26 17:31:57 +03:00
icon={<IconEdit2 size='1rem' className='icon-primary' />}
disabled={!controller.isMutable || isProcessing}
2024-07-26 17:31:57 +03:00
onClick={handleEditOperation}
/>
{operation.result ? (
<DropdownButton
text='Открыть схему'
titleHtml={prepareTooltip('Открыть привязанную КС', 'Двойной клик')}
2024-07-26 17:31:57 +03:00
icon={<IconRSForm size='1rem' className='icon-green' />}
disabled={isProcessing}
2024-07-26 17:31:57 +03:00
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' />}
disabled={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' />}
disabled={isProcessing}
2024-07-26 17:31:57 +03:00
onClick={handleEditSchema}
/>
) : null}
{controller.isMutable && !operation.result && operation.operation_type === OperationType.SYNTHESIS ? (
<DropdownButton
2024-08-27 11:34:36 +03:00
text='Активировать синтез'
titleHtml={
2024-07-29 16:56:24 +03:00
readyForSynthesis
2024-08-27 11:34:36 +03:00
? 'Активировать операцию<br/>и получить синтезированную КС'
2024-07-29 16:56:24 +03:00
: 'Необходимо предоставить все аргументы'
}
icon={<IconExecute size='1rem' className='icon-green' />}
disabled={isProcessing || !readyForSynthesis}
onClick={handleRunSynthesis}
/>
) : null}
2024-07-26 17:31:57 +03:00
{controller.isMutable && operation.result ? (
<DropdownButton
text='Конституенты'
2024-10-29 12:06:43 +03:00
titleHtml='Перенос конституент</br>между схемами'
icon={<IconChild size='1rem' className='icon-green' />}
disabled={isProcessing}
onClick={handleRelocateConstituents}
/>
) : null}
2024-07-26 17:31:57 +03:00
<DropdownButton
text='Удалить операцию'
icon={<IconDestroy size='1rem' className='icon-red' />}
disabled={!controller.isMutable || isProcessing || !controller.canDelete(operation.id)}
2024-07-26 17:31:57 +03:00
onClick={handleDeleteOperation}
/>
</Dropdown>
</div>
);
}
export default NodeContextMenu;