diff --git a/rsconcept/backend/apps/oss/models/OperationSchema.py b/rsconcept/backend/apps/oss/models/OperationSchema.py index e8dc9839..71cc2e77 100644 --- a/rsconcept/backend/apps/oss/models/OperationSchema.py +++ b/rsconcept/backend/apps/oss/models/OperationSchema.py @@ -1,13 +1,11 @@ ''' Models: OSS API. ''' from typing import Optional -from django.core.exceptions import ValidationError from django.db import transaction from django.db.models import QuerySet from apps.library.models import Editor, LibraryItem, LibraryItemType from apps.rsform.models import RSForm -from shared import messages as msg from .Argument import Argument from .Inheritance import Inheritance @@ -66,8 +64,6 @@ class OperationSchema: @transaction.atomic def create_operation(self, **kwargs) -> Operation: ''' Insert new operation. ''' - if kwargs['alias'] != '' and self.operations().filter(alias=kwargs['alias']).exists(): - raise ValidationError(msg.aliasTaken(kwargs['alias'])) result = Operation.objects.create(oss=self.model, **kwargs) self.save() result.refresh_from_db() diff --git a/rsconcept/frontend/src/components/info/TooltipOperation.tsx b/rsconcept/frontend/src/components/info/TooltipOperation.tsx index b1c42b22..babfe7aa 100644 --- a/rsconcept/frontend/src/components/info/TooltipOperation.tsx +++ b/rsconcept/frontend/src/components/info/TooltipOperation.tsx @@ -1,13 +1,66 @@ +'use client'; + +import { createColumnHelper } from '@tanstack/react-table'; +import { useMemo } from 'react'; + import Tooltip from '@/components/ui/Tooltip'; import { OssNodeInternal } from '@/models/miscellaneous'; +import { ICstSubstituteEx } from '@/models/oss'; import { labelOperationType } from '@/utils/labels'; +import { IconPageRight } from '../Icons'; +import DataTable from '../ui/DataTable'; + interface TooltipOperationProps { node: OssNodeInternal; anchor: string; } +const columnHelper = createColumnHelper(); + function TooltipOperation({ node, anchor }: TooltipOperationProps) { + const columns = useMemo( + () => [ + columnHelper.accessor('substitution_term', { + id: 'substitution_term', + size: 200 + }), + columnHelper.accessor('substitution_alias', { + id: 'substitution_alias', + size: 50 + }), + columnHelper.display({ + id: 'status', + header: '', + size: 40, + cell: () => + }), + columnHelper.accessor('original_alias', { + id: 'original_alias', + size: 50 + }), + columnHelper.accessor('original_term', { + id: 'original_term', + size: 200 + }) + ], + [] + ); + + const table = useMemo( + () => ( + + ), + [columns, node] + ); + return (

{node.data.operation.alias}

@@ -29,6 +82,7 @@ function TooltipOperation({ node, anchor }: TooltipOperationProps) {

Положение: [{node.xPos}, {node.yPos}]

+ {node.data.operation.substitutions.length > 0 ? table : null}
); } diff --git a/rsconcept/frontend/src/components/select/MiniSelectorOSS.tsx b/rsconcept/frontend/src/components/select/MiniSelectorOSS.tsx new file mode 100644 index 00000000..435753ca --- /dev/null +++ b/rsconcept/frontend/src/components/select/MiniSelectorOSS.tsx @@ -0,0 +1,43 @@ +'use client'; + +import { IconOSS } from '@/components/Icons'; +import { CProps } from '@/components/props'; +import Dropdown from '@/components/ui/Dropdown'; +import DropdownButton from '@/components/ui/DropdownButton'; +import Label from '@/components/ui/Label'; +import MiniButton from '@/components/ui/MiniButton'; +import useDropdown from '@/hooks/useDropdown'; +import { ILibraryItemReference } from '@/models/library'; +import { prefixes } from '@/utils/constants'; + +interface MiniSelectorOSSProps { + items: ILibraryItemReference[]; + onSelect: (event: CProps.EventMouse, newValue: ILibraryItemReference) => void; +} + +function MiniSelectorOSS({ items, onSelect }: MiniSelectorOSSProps) { + const ossMenu = useDropdown(); + return ( +
+ } + title='Связанные операционные схемы' + hideTitle={ossMenu.isOpen} + onClick={() => ossMenu.toggle()} + /> + + +
+ ); +} + +export default MiniSelectorOSS; diff --git a/rsconcept/frontend/src/context/OssContext.tsx b/rsconcept/frontend/src/context/OssContext.tsx index f0c0c039..dc54e646 100644 --- a/rsconcept/frontend/src/context/OssContext.tsx +++ b/rsconcept/frontend/src/context/OssContext.tsx @@ -25,8 +25,8 @@ import { type ErrorData } from '@/components/info/InfoError'; import { AccessPolicy, ILibraryItem } from '@/models/library'; import { ILibraryUpdateData } from '@/models/library'; import { - IOperation, IOperationCreateData, + IOperationData, IOperationSchema, IOperationSchemaData, IOperationSetInputData, @@ -62,7 +62,7 @@ interface IOssContext { setEditors: (newEditors: UserID[], callback?: () => void) => void; savePositions: (data: IPositionsData, callback?: () => void) => void; - createOperation: (data: IOperationCreateData, callback?: DataCallback) => void; + createOperation: (data: IOperationCreateData, callback?: DataCallback) => void; deleteOperation: (data: ITargetOperation, callback?: () => void) => void; createInput: (data: ITargetOperation, callback?: DataCallback) => void; setInput: (data: IOperationSetInputData, callback?: () => void) => void; @@ -292,7 +292,7 @@ export const OssState = ({ itemID, children }: OssStateProps) => { ); const createOperation = useCallback( - (data: IOperationCreateData, callback?: DataCallback) => { + (data: IOperationCreateData, callback?: DataCallback) => { setProcessingError(undefined); postCreateOperation(itemID, { data: data, diff --git a/rsconcept/frontend/src/dialogs/DlgCreateOperation/DlgCreateOperation.tsx b/rsconcept/frontend/src/dialogs/DlgCreateOperation/DlgCreateOperation.tsx index 6fb90496..4d6533f0 100644 --- a/rsconcept/frontend/src/dialogs/DlgCreateOperation/DlgCreateOperation.tsx +++ b/rsconcept/frontend/src/dialogs/DlgCreateOperation/DlgCreateOperation.tsx @@ -1,7 +1,7 @@ 'use client'; import clsx from 'clsx'; -import { useLayoutEffect, useMemo, useState } from 'react'; +import { useCallback, useLayoutEffect, useMemo, useState } from 'react'; import { TabList, TabPanel, Tabs } from 'react-tabs'; import BadgeHelp from '@/components/info/BadgeHelp'; @@ -22,6 +22,7 @@ interface DlgCreateOperationProps { hideWindow: () => void; oss: IOperationSchema; onCreate: (data: IOperationCreateData) => void; + initialInputs: OperationID[]; } export enum TabID { @@ -29,21 +30,31 @@ export enum TabID { SYNTHESIS = 1 } -function DlgCreateOperation({ hideWindow, oss, onCreate }: DlgCreateOperationProps) { +function DlgCreateOperation({ hideWindow, oss, onCreate, initialInputs }: DlgCreateOperationProps) { const library = useLibrary(); - const [activeTab, setActiveTab] = useState(TabID.INPUT); + const [activeTab, setActiveTab] = useState(initialInputs.length > 0 ? TabID.SYNTHESIS : TabID.INPUT); const [alias, setAlias] = useState(''); const [title, setTitle] = useState(''); const [comment, setComment] = useState(''); - const [inputs, setInputs] = useState([]); + const [inputs, setInputs] = useState(initialInputs); const [attachedID, setAttachedID] = useState(undefined); const [createSchema, setCreateSchema] = useState(false); - const isValid = useMemo( - () => (alias !== '' && activeTab === TabID.INPUT) || inputs.length != 1, - [alias, activeTab, inputs] - ); + const isValid = useMemo(() => { + if (alias === '') { + return false; + } + if (activeTab === TabID.SYNTHESIS && inputs.length === 1) { + return false; + } + if (activeTab === TabID.INPUT && !attachedID) { + if (oss.items.some(operation => operation.alias === alias)) { + return false; + } + } + return true; + }, [alias, activeTab, inputs, attachedID, oss.items]); useLayoutEffect(() => { if (attachedID) { @@ -74,6 +85,21 @@ function DlgCreateOperation({ hideWindow, oss, onCreate }: DlgCreateOperationPro onCreate(data); }; + const handleSelectTab = useCallback( + (newTab: TabID, last: TabID) => { + if (last === newTab) { + return; + } + if (newTab === TabID.INPUT) { + setAttachedID(undefined); + } else { + setInputs(initialInputs); + } + setActiveTab(newTab); + }, + [setActiveTab, initialInputs] + ); + const inputPanel = useMemo( () => ( @@ -131,7 +157,7 @@ function DlgCreateOperation({ hideWindow, oss, onCreate }: DlgCreateOperationPro selectedTabClassName='clr-selected' className='flex flex-col' selectedIndex={activeTab} - onSelect={setActiveTab} + onSelect={handleSelectTab} > inputOperations.map(operation => operation.result).filter(id => id !== null), [inputOperations] ); - const [substitutions, setSubstitutions] = useState(oss.substitutions); + const [substitutions, setSubstitutions] = useState(target.substitutions); const cache = useRSFormCache(); const schemas = useMemo( () => schemasIDs.map(id => cache.getSchema(id)).filter(item => item !== undefined), diff --git a/rsconcept/frontend/src/models/OssLoader.ts b/rsconcept/frontend/src/models/OssLoader.ts index f6107dde..3fca317f 100644 --- a/rsconcept/frontend/src/models/OssLoader.ts +++ b/rsconcept/frontend/src/models/OssLoader.ts @@ -32,6 +32,7 @@ export class OssLoader { this.prepareLookups(); this.createGraph(); this.extractSchemas(); + this.inferOperationAttributes(); result.operationByID = this.operationByID; result.graph = this.graph; @@ -42,7 +43,7 @@ export class OssLoader { private prepareLookups() { this.oss.items.forEach(operation => { - this.operationByID.set(operation.id, operation); + this.operationByID.set(operation.id, operation as IOperation); this.graph.addNode(operation.id); }); } @@ -55,6 +56,16 @@ export class OssLoader { this.schemas = this.oss.items.map(operation => operation.result as LibraryItemID).filter(item => item !== null); } + private inferOperationAttributes() { + this.graph.topologicalOrder().forEach(operationID => { + const operation = this.operationByID.get(operationID)!; + operation.substitutions = this.oss.substitutions.filter(item => item.operation === operationID); + operation.arguments = this.oss.arguments + .filter(item => item.operation === operationID) + .map(item => item.argument); + }); + } + private calculateStats(): IOperationSchemaStats { const items = this.oss.items; return { diff --git a/rsconcept/frontend/src/models/oss.ts b/rsconcept/frontend/src/models/oss.ts index 1431d0f4..c818b413 100644 --- a/rsconcept/frontend/src/models/oss.ts +++ b/rsconcept/frontend/src/models/oss.ts @@ -35,8 +35,16 @@ export interface IOperation { position_y: number; result: LibraryItemID | null; + + substitutions: ICstSubstituteEx[]; + arguments: OperationID[]; } +/** + * Represents {@link IOperation} data from server. + */ +export interface IOperationData extends Omit {} + /** * Represents {@link IOperation} position. */ @@ -121,6 +129,7 @@ export interface IMultiSubstitution { * Represents {@link ICstSubstitute} extended data. */ export interface ICstSubstituteEx extends ICstSubstitute { + operation: OperationID; original_alias: string; original_term: string; substitution_alias: string; @@ -141,7 +150,7 @@ export interface IOperationSchemaStats { * Represents backend data for {@link IOperationSchema}. */ export interface IOperationSchemaData extends ILibraryItemData { - items: IOperation[]; + items: IOperationData[]; arguments: IArgument[]; substitutions: ICstSubstituteEx[]; } @@ -150,6 +159,7 @@ export interface IOperationSchemaData extends ILibraryItemData { * Represents OperationSchema. */ export interface IOperationSchema extends IOperationSchemaData { + items: IOperation[]; graph: Graph; schemas: LibraryItemID[]; stats: IOperationSchemaStats; @@ -160,7 +170,7 @@ export interface IOperationSchema extends IOperationSchemaData { * Represents data response when creating {@link IOperation}. */ export interface IOperationCreatedResponse { - new_operation: IOperation; + new_operation: IOperationData; oss: IOperationSchemaData; } diff --git a/rsconcept/frontend/src/models/rsform.ts b/rsconcept/frontend/src/models/rsform.ts index 387ccd73..7778f525 100644 --- a/rsconcept/frontend/src/models/rsform.ts +++ b/rsconcept/frontend/src/models/rsform.ts @@ -91,7 +91,7 @@ export interface ITargetCst { } /** - * Represents Constituenta data from server. + * Represents {@link IConstituenta} data from server. */ export interface IConstituentaData extends IConstituentaMeta { parse: { diff --git a/rsconcept/frontend/src/pages/CreateItemPage/FormCreateItem.tsx b/rsconcept/frontend/src/pages/CreateItemPage/FormCreateItem.tsx index b5f909a8..cf0bdd78 100644 --- a/rsconcept/frontend/src/pages/CreateItemPage/FormCreateItem.tsx +++ b/rsconcept/frontend/src/pages/CreateItemPage/FormCreateItem.tsx @@ -1,7 +1,7 @@ 'use client'; import clsx from 'clsx'; -import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'; import { toast } from 'react-toastify'; import { urls } from '@/app/urls'; @@ -22,10 +22,11 @@ import TextInput from '@/components/ui/TextInput'; import { useAuth } from '@/context/AuthContext'; import { useLibrary } from '@/context/LibraryContext'; import { useConceptNavigation } from '@/context/NavigationContext'; +import useLocalStorage from '@/hooks/useLocalStorage'; import { AccessPolicy, LibraryItemType, LocationHead } from '@/models/library'; import { ILibraryCreateData } from '@/models/library'; import { combineLocation, validateLocation } from '@/models/libraryAPI'; -import { EXTEOR_TRS_FILE, limits, patterns } from '@/utils/constants'; +import { EXTEOR_TRS_FILE, limits, patterns, storage } from '@/utils/constants'; import { information } from '@/utils/labels'; function FormCreateItem() { @@ -45,6 +46,7 @@ function FormCreateItem() { const location = useMemo(() => combineLocation(head, body), [head, body]); const isValid = useMemo(() => validateLocation(location), [location]); + const [initLocation] = useLocalStorage(storage.librarySearchLocation, ''); const [fileName, setFileName] = useState(''); const [file, setFile] = useState(); @@ -104,6 +106,13 @@ function FormCreateItem() { setBody(newValue.length > 3 ? newValue.substring(3) : ''); }, []); + useLayoutEffect(() => { + if (!initLocation) { + return; + } + handleSelectLocation(initLocation); + }, [initLocation, handleSelectLocation]); + return (

diff --git a/rsconcept/frontend/src/pages/ManualsPage/items/HelpCstEditor.tsx b/rsconcept/frontend/src/pages/ManualsPage/items/HelpCstEditor.tsx index a2a45cbb..ef00b3af 100644 --- a/rsconcept/frontend/src/pages/ManualsPage/items/HelpCstEditor.tsx +++ b/rsconcept/frontend/src/pages/ManualsPage/items/HelpCstEditor.tsx @@ -7,6 +7,7 @@ import { IconMoveDown, IconMoveUp, IconNewItem, + IconOSS, IconReset, IconSave, IconStatusOK, @@ -22,6 +23,9 @@ function HelpCstEditor() { return (

Редактор конституенты

+
  • + переход к связанной +
  • сохранить изменения: Ctrl + S
  • diff --git a/rsconcept/frontend/src/pages/ManualsPage/items/HelpRSFormCard.tsx b/rsconcept/frontend/src/pages/ManualsPage/items/HelpRSFormCard.tsx index e85e99c6..380b6b83 100644 --- a/rsconcept/frontend/src/pages/ManualsPage/items/HelpRSFormCard.tsx +++ b/rsconcept/frontend/src/pages/ManualsPage/items/HelpRSFormCard.tsx @@ -5,6 +5,7 @@ import { IconEditor, IconFollow, IconImmutable, + IconOSS, IconOwner, IconPublic, IconSave @@ -29,6 +30,9 @@ function HelpRSFormCard() {

    Управление

    +
  • + переход к связанной +
  • сохранить изменения: Ctrl + S
  • diff --git a/rsconcept/frontend/src/pages/ManualsPage/items/HelpRSFormItems.tsx b/rsconcept/frontend/src/pages/ManualsPage/items/HelpRSFormItems.tsx index 75a1f5a1..f21f4758 100644 --- a/rsconcept/frontend/src/pages/ManualsPage/items/HelpRSFormItems.tsx +++ b/rsconcept/frontend/src/pages/ManualsPage/items/HelpRSFormItems.tsx @@ -6,6 +6,7 @@ import { IconMoveUp, IconNewItem, IconOpenList, + IconOSS, IconReset } from '@/components/Icons'; import InfoCstStatus from '@/components/info/InfoCstStatus'; @@ -27,6 +28,9 @@ function HelpRSFormItems() {

    Управление списком

    +
  • + переход к связанной +
  • сбросить выделение: ESC
  • diff --git a/rsconcept/frontend/src/pages/ManualsPage/items/HelpTermGraph.tsx b/rsconcept/frontend/src/pages/ManualsPage/items/HelpTermGraph.tsx index 1315f0d4..e113d458 100644 --- a/rsconcept/frontend/src/pages/ManualsPage/items/HelpTermGraph.tsx +++ b/rsconcept/frontend/src/pages/ManualsPage/items/HelpTermGraph.tsx @@ -12,6 +12,7 @@ import { IconGraphOutputs, IconImage, IconNewItem, + IconOSS, IconReset, IconRotate3D, IconText @@ -70,6 +71,9 @@ function HelpTermGraph() {

    Общие

    +
  • + переход к связанной +
  • Открыть настройки
  • diff --git a/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/NodeContextMenu.tsx b/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/NodeContextMenu.tsx index 781d2d57..a3ae9d2b 100644 --- a/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/NodeContextMenu.tsx +++ b/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/NodeContextMenu.tsx @@ -101,11 +101,11 @@ function NodeContextMenu({ }; return ( -
    +
    = window.innerWidth - PARAMETER.ossContextMenuWidth}> } disabled={controller.isProcessing} onClick={handleEditOperation} diff --git a/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/OssFlow.tsx b/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/OssFlow.tsx index d0c8621a..6ba1c55e 100644 --- a/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/OssFlow.tsx +++ b/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/OssFlow.tsx @@ -122,10 +122,43 @@ function OssFlow({ isModified, setIsModified }: OssFlowProps) { controller.savePositions(getPositions(), () => setIsModified(false)); }, [controller, getPositions, setIsModified]); - const handleCreateOperation = useCallback(() => { - const center = flow.project({ x: window.innerWidth / 2, y: window.innerHeight / 2 }); - controller.promptCreateOperation(center.x, center.y, getPositions()); - }, [controller, getPositions, flow]); + const handleCreateOperation = useCallback( + (inputs: OperationID[]) => () => { + if (!controller.schema) { + return; + } + let target = { x: 0, y: 0 }; + const positions = getPositions(); + + if (inputs.length <= 1) { + target = flow.project({ x: window.innerWidth / 2, y: window.innerHeight / 2 }); + } else { + const inputsNodes = positions.filter(pos => inputs.includes(pos.id)); + const maxY = Math.max(...inputsNodes.map(node => node.position_y)); + const minX = Math.min(...inputsNodes.map(node => node.position_x)); + const maxX = Math.max(...inputsNodes.map(node => node.position_x)); + + target.y = maxY + 100; + target.x = Math.ceil((maxX + minX) / 2 / PARAMETER.ossGridSize) * PARAMETER.ossGridSize; + } + + let flagIntersect = false; + do { + flagIntersect = positions.some( + position => + Math.abs(position.position_x - target.x) < PARAMETER.ossMinDistance && + Math.abs(position.position_y - target.y) < PARAMETER.ossMinDistance + ); + if (flagIntersect) { + target.x += PARAMETER.ossMinDistance; + target.y += PARAMETER.ossMinDistance; + } + } while (flagIntersect); + + controller.promptCreateOperation(target.x, target.y, inputs, positions); + }, + [controller, getPositions, flow] + ); const handleDeleteSelected = useCallback(() => { if (controller.selected.length !== 1) { @@ -241,13 +274,11 @@ function OssFlow({ isModified, setIsModified }: OssFlowProps) { handleContextMenuHide(); }, [handleContextMenuHide]); - const handleNodeClick = useCallback( + const handleNodeDoubleClick = useCallback( (event: CProps.EventMouse, node: OssNode) => { - if (event.ctrlKey || event.metaKey) { - event.preventDefault(); - event.stopPropagation(); - handleEditOperation(Number(node.id)); - } + event.preventDefault(); + event.stopPropagation(); + handleEditOperation(Number(node.id)); }, [handleEditOperation] ); @@ -268,7 +299,7 @@ function OssFlow({ isModified, setIsModified }: OssFlowProps) { if ((event.ctrlKey || event.metaKey) && event.key === 'q') { event.preventDefault(); event.stopPropagation(); - handleCreateOperation(); + handleCreateOperation(controller.selected); return; } if (event.key === 'Delete') { @@ -297,7 +328,7 @@ function OssFlow({ isModified, setIsModified }: OssFlowProps) { edges={edges} onNodesChange={handleNodesChange} onEdgesChange={onEdgesChange} - onNodeClick={handleNodeClick} + onNodeDoubleClick={handleNodeDoubleClick} proOptions={{ hideAttribution: true }} fitView nodeTypes={OssNodeTypes} @@ -305,11 +336,11 @@ function OssFlow({ isModified, setIsModified }: OssFlowProps) { minZoom={0.75} nodesConnectable={false} snapToGrid={true} - snapGrid={[10, 10]} + snapGrid={[PARAMETER.ossGridSize, PARAMETER.ossGridSize]} onNodeContextMenu={handleContextMenu} onClick={handleClickCanvas} > - {showGrid ? : null} + {showGrid ? : null} ), [ @@ -319,7 +350,7 @@ function OssFlow({ isModified, setIsModified }: OssFlowProps) { handleContextMenu, handleClickCanvas, onEdgesChange, - handleNodeClick, + handleNodeDoubleClick, OssNodeTypes, showGrid ] @@ -334,7 +365,7 @@ function OssFlow({ isModified, setIsModified }: OssFlowProps) { edgeAnimate={edgeAnimate} edgeStraight={edgeStraight} onFitView={handleFitView} - onCreate={handleCreateOperation} + onCreate={handleCreateOperation(controller.selected)} onDelete={handleDeleteSelected} onEdit={() => handleEditOperation(controller.selected[0])} onExecute={handleExecuteSelected} diff --git a/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/ToolbarOssGraph.tsx b/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/ToolbarOssGraph.tsx index 2a2b5b9d..fdf91dd8 100644 --- a/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/ToolbarOssGraph.tsx +++ b/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/ToolbarOssGraph.tsx @@ -167,7 +167,7 @@ function ToolbarOssGraph({ onClick={onExecute} /> } disabled={controller.selected.length !== 1 || controller.isProcessing} onClick={onEdit} diff --git a/rsconcept/frontend/src/pages/OssPage/OssEditContext.tsx b/rsconcept/frontend/src/pages/OssPage/OssEditContext.tsx index 53c7975c..f4a20896 100644 --- a/rsconcept/frontend/src/pages/OssPage/OssEditContext.tsx +++ b/rsconcept/frontend/src/pages/OssPage/OssEditContext.tsx @@ -51,7 +51,7 @@ export interface IOssEditContext { openOperationSchema: (target: OperationID) => void; savePositions: (positions: IOperationPosition[], callback?: () => void) => void; - promptCreateOperation: (x: number, y: number, positions: IOperationPosition[]) => void; + promptCreateOperation: (x: number, y: number, inputs: OperationID[], positions: IOperationPosition[]) => void; deleteOperation: (target: OperationID, positions: IOperationPosition[]) => void; createInput: (target: OperationID, positions: IOperationPosition[]) => void; promptEditInput: (target: OperationID, positions: IOperationPosition[]) => void; @@ -96,6 +96,7 @@ export const OssEditState = ({ selected, setSelected, children }: OssEditStatePr const [showCreateOperation, setShowCreateOperation] = useState(false); const [insertPosition, setInsertPosition] = useState({ x: 0, y: 0 }); + const [initialInputs, setInitialInputs] = useState([]); const [positions, setPositions] = useState([]); const [targetOperationID, setTargetOperationID] = useState(undefined); const targetOperation = useMemo( @@ -208,11 +209,15 @@ export const OssEditState = ({ selected, setSelected, children }: OssEditStatePr [model] ); - const promptCreateOperation = useCallback((x: number, y: number, positions: IOperationPosition[]) => { - setInsertPosition({ x: x, y: y }); - setPositions(positions); - setShowCreateOperation(true); - }, []); + const promptCreateOperation = useCallback( + (x: number, y: number, inputs: OperationID[], positions: IOperationPosition[]) => { + setInsertPosition({ x: x, y: y }); + setInitialInputs(inputs); + setPositions(positions); + setShowCreateOperation(true); + }, + [] + ); const handleCreateOperation = useCallback( (data: IOperationCreateData) => { @@ -341,6 +346,7 @@ export const OssEditState = ({ selected, setSelected, children }: OssEditStatePr hideWindow={() => setShowCreateOperation(false)} oss={model.schema} onCreate={handleCreateOperation} + initialInputs={initialInputs} /> ) : null} {showEditInput ? ( diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta/ToolbarConstituenta.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta/ToolbarConstituenta.tsx index 2192569a..77397715 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta/ToolbarConstituenta.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta/ToolbarConstituenta.tsx @@ -12,6 +12,7 @@ import { IconSave } from '@/components/Icons'; import BadgeHelp from '@/components/info/BadgeHelp'; +import MiniSelectorOSS from '@/components/select/MiniSelectorOSS'; import MiniButton from '@/components/ui/MiniButton'; import Overlay from '@/components/ui/Overlay'; import { HelpTopic } from '@/models/miscellaneous'; @@ -54,6 +55,12 @@ function ToolbarConstituenta({ return ( + {controller.schema && controller.schema?.oss.length > 0 ? ( + controller.viewOSS(value.id, event.ctrlKey || event.metaKey)} + /> + ) : null} } diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorRSFormCard/FormRSForm.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorRSFormCard/FormRSForm.tsx index 0cde98ef..1dbe513b 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorRSFormCard/FormRSForm.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorRSFormCard/FormRSForm.tsx @@ -1,21 +1,16 @@ 'use client'; import clsx from 'clsx'; -import { useEffect, useLayoutEffect, useMemo, useState } from 'react'; +import { useEffect, useLayoutEffect, useState } from 'react'; -import { IconOSS, IconSave } from '@/components/Icons'; +import { IconSave } from '@/components/Icons'; import SelectVersion from '@/components/select/SelectVersion'; -import Dropdown from '@/components/ui/Dropdown'; -import DropdownButton from '@/components/ui/DropdownButton'; import Label from '@/components/ui/Label'; -import MiniButton from '@/components/ui/MiniButton'; -import Overlay from '@/components/ui/Overlay'; import SubmitButton from '@/components/ui/SubmitButton'; import TextArea from '@/components/ui/TextArea'; import TextInput from '@/components/ui/TextInput'; -import useDropdown from '@/hooks/useDropdown'; import { ILibraryUpdateData, LibraryItemType } from '@/models/library'; -import { limits, patterns, prefixes } from '@/utils/constants'; +import { limits, patterns } from '@/utils/constants'; import { useRSEdit } from '../RSEditContext'; import ToolbarItemAccess from './ToolbarItemAccess'; @@ -37,8 +32,6 @@ function FormRSForm({ id, isModified, setIsModified }: FormRSFormProps) { const [visible, setVisible] = useState(false); const [readOnly, setReadOnly] = useState(false); - const ossMenu = useDropdown(); - useEffect(() => { if (!schema) { setIsModified(false); @@ -92,35 +85,6 @@ function FormRSForm({ id, isModified, setIsModified }: FormRSFormProps) { controller.updateSchema(data); }; - const ossSelector = useMemo( - () => - schema && schema?.oss.length > 0 ? ( - -
    - } - noHover - title='Связанные операционные схемы' - hideTitle={ossMenu.isOpen} - onClick={() => ossMenu.toggle()} - /> - - -
    -
    - ) : null, - [schema, ossMenu, controller] - ); - return ( setTitle(event.target.value)} /> - {ossSelector}
    +