mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 21:10:38 +03:00
F: Implement UI for synthesis
This commit is contained in:
parent
591d36d772
commit
5ee85b169d
|
@ -0,0 +1,273 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
|
|
||||||
|
import BadgeConstituenta from '@/components/info/BadgeConstituenta';
|
||||||
|
import SelectConstituenta from '@/components/select/SelectConstituenta';
|
||||||
|
import DataTable, { createColumnHelper } from '@/components/ui/DataTable';
|
||||||
|
import Label from '@/components/ui/Label';
|
||||||
|
import MiniButton from '@/components/ui/MiniButton';
|
||||||
|
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
||||||
|
import { IBinarySubstitution, IConstituenta, IRSForm } from '@/models/rsform';
|
||||||
|
import { describeConstituenta } from '@/utils/labels';
|
||||||
|
|
||||||
|
import {
|
||||||
|
IconKeepAliasOff,
|
||||||
|
IconKeepAliasOn,
|
||||||
|
IconKeepTermOff,
|
||||||
|
IconKeepTermOn,
|
||||||
|
IconPageFirst,
|
||||||
|
IconPageLast,
|
||||||
|
IconPageLeft,
|
||||||
|
IconPageRight,
|
||||||
|
IconRemove,
|
||||||
|
IconReplace
|
||||||
|
} from '../Icons';
|
||||||
|
import NoData from '../ui/NoData';
|
||||||
|
|
||||||
|
interface PickInlineSubstitutionsProps {
|
||||||
|
prefixID: string;
|
||||||
|
rows?: number;
|
||||||
|
|
||||||
|
schema1?: IRSForm;
|
||||||
|
schema2?: IRSForm;
|
||||||
|
filter1?: (cst: IConstituenta) => boolean;
|
||||||
|
filter2?: (cst: IConstituenta) => boolean;
|
||||||
|
|
||||||
|
items: IBinarySubstitution[];
|
||||||
|
setItems: React.Dispatch<React.SetStateAction<IBinarySubstitution[]>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function SubstitutionIcon({ item }: { item: IBinarySubstitution }) {
|
||||||
|
if (item.deleteRight) {
|
||||||
|
if (item.takeLeftTerm) {
|
||||||
|
return <IconPageRight size='1.2rem' />;
|
||||||
|
} else {
|
||||||
|
return <IconPageLast size='1.2rem' />;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (item.takeLeftTerm) {
|
||||||
|
return <IconPageFirst size='1.2rem' />;
|
||||||
|
} else {
|
||||||
|
return <IconPageLeft size='1.2rem' />;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const columnHelper = createColumnHelper<IBinarySubstitution>();
|
||||||
|
|
||||||
|
function PickInlineSubstitutions({
|
||||||
|
items,
|
||||||
|
schema1,
|
||||||
|
schema2,
|
||||||
|
filter1,
|
||||||
|
filter2,
|
||||||
|
rows,
|
||||||
|
setItems,
|
||||||
|
prefixID
|
||||||
|
}: PickInlineSubstitutionsProps) {
|
||||||
|
const { colors } = useConceptOptions();
|
||||||
|
|
||||||
|
const [leftCst, setLeftCst] = useState<IConstituenta | undefined>(undefined);
|
||||||
|
const [rightCst, setRightCst] = useState<IConstituenta | undefined>(undefined);
|
||||||
|
const [deleteRight, setDeleteRight] = useState(true);
|
||||||
|
const [takeLeftTerm, setTakeLeftTerm] = useState(true);
|
||||||
|
|
||||||
|
const toggleDelete = () => setDeleteRight(prev => !prev);
|
||||||
|
const toggleTerm = () => setTakeLeftTerm(prev => !prev);
|
||||||
|
|
||||||
|
function addSubstitution() {
|
||||||
|
if (!leftCst || !rightCst) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const newSubstitution: IBinarySubstitution = {
|
||||||
|
leftCst: leftCst,
|
||||||
|
rightCst: rightCst,
|
||||||
|
deleteRight: deleteRight,
|
||||||
|
takeLeftTerm: takeLeftTerm
|
||||||
|
};
|
||||||
|
setItems([
|
||||||
|
newSubstitution,
|
||||||
|
...items.filter(
|
||||||
|
item =>
|
||||||
|
(!item.deleteRight && item.leftCst.id !== leftCst.id) ||
|
||||||
|
(item.deleteRight && item.rightCst.id !== rightCst.id)
|
||||||
|
)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDeleteRow = useCallback(
|
||||||
|
(row: number) => {
|
||||||
|
setItems(prev => {
|
||||||
|
const newItems: IBinarySubstitution[] = [];
|
||||||
|
prev.forEach((item, index) => {
|
||||||
|
if (index !== row) {
|
||||||
|
newItems.push(item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return newItems;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[setItems]
|
||||||
|
);
|
||||||
|
|
||||||
|
const columns = useMemo(
|
||||||
|
() => [
|
||||||
|
columnHelper.accessor(item => describeConstituenta(item.leftCst), {
|
||||||
|
id: 'left_text',
|
||||||
|
header: 'Описание',
|
||||||
|
size: 1000,
|
||||||
|
cell: props => <div className='text-xs text-ellipsis'>{props.getValue()}</div>
|
||||||
|
}),
|
||||||
|
columnHelper.accessor(item => item.leftCst.alias, {
|
||||||
|
id: 'left_alias',
|
||||||
|
header: () => <span className='pl-3'>Имя</span>,
|
||||||
|
size: 65,
|
||||||
|
cell: props => (
|
||||||
|
<BadgeConstituenta theme={colors} value={props.row.original.leftCst} prefixID={`${prefixID}_1_`} />
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
columnHelper.display({
|
||||||
|
id: 'status',
|
||||||
|
header: '',
|
||||||
|
size: 40,
|
||||||
|
cell: props => <SubstitutionIcon item={props.row.original} />
|
||||||
|
}),
|
||||||
|
columnHelper.accessor(item => item.rightCst.alias, {
|
||||||
|
id: 'right_alias',
|
||||||
|
header: () => <span className='pl-3'>Имя</span>,
|
||||||
|
size: 65,
|
||||||
|
cell: props => (
|
||||||
|
<BadgeConstituenta theme={colors} value={props.row.original.rightCst} prefixID={`${prefixID}_2_`} />
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
columnHelper.accessor(item => describeConstituenta(item.rightCst), {
|
||||||
|
id: 'right_text',
|
||||||
|
header: 'Описание',
|
||||||
|
minSize: 1000,
|
||||||
|
cell: props => <div className='text-xs text-ellipsis text-pretty'>{props.getValue()}</div>
|
||||||
|
}),
|
||||||
|
columnHelper.display({
|
||||||
|
id: 'actions',
|
||||||
|
cell: props => (
|
||||||
|
<MiniButton
|
||||||
|
noHover
|
||||||
|
title='Удалить'
|
||||||
|
icon={<IconRemove size='1rem' className='icon-red' />}
|
||||||
|
onClick={() => handleDeleteRow(props.row.index)}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
],
|
||||||
|
[handleDeleteRow, colors, prefixID]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='flex flex-col w-full'>
|
||||||
|
<div className='flex items-end gap-3 justify-stretch'>
|
||||||
|
<div className='flex-grow basis-1/2'>
|
||||||
|
<div className='flex items-center justify-between'>
|
||||||
|
<Label text={schema1 !== schema2 ? schema1?.alias ?? 'Схема 1' : ''} />
|
||||||
|
<div className='cc-icons'>
|
||||||
|
<MiniButton
|
||||||
|
title='Сохранить конституенту'
|
||||||
|
noHover
|
||||||
|
onClick={toggleDelete}
|
||||||
|
icon={
|
||||||
|
deleteRight ? (
|
||||||
|
<IconKeepAliasOn size='1rem' className='clr-text-green' />
|
||||||
|
) : (
|
||||||
|
<IconKeepAliasOff size='1rem' className='clr-text-red' />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<MiniButton
|
||||||
|
title='Сохранить термин'
|
||||||
|
noHover
|
||||||
|
onClick={toggleTerm}
|
||||||
|
icon={
|
||||||
|
takeLeftTerm ? (
|
||||||
|
<IconKeepTermOn size='1rem' className='clr-text-green' />
|
||||||
|
) : (
|
||||||
|
<IconKeepTermOff size='1rem' className='clr-text-red' />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<SelectConstituenta
|
||||||
|
items={schema1?.items.filter(cst => !filter1 || filter1(cst))}
|
||||||
|
value={leftCst}
|
||||||
|
onSelectValue={setLeftCst}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<MiniButton
|
||||||
|
noHover
|
||||||
|
title='Добавить в таблицу отождествлений'
|
||||||
|
className='mb-[0.375rem] grow-0'
|
||||||
|
icon={<IconReplace size='1.5rem' className='icon-primary' />}
|
||||||
|
disabled={!leftCst || !rightCst || leftCst === rightCst}
|
||||||
|
onClick={addSubstitution}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className='flex-grow basis-1/2'>
|
||||||
|
<div className='flex items-center justify-between'>
|
||||||
|
<Label text={schema1 !== schema2 ? schema2?.alias ?? 'Схема 2' : ''} />
|
||||||
|
<div className='cc-icons'>
|
||||||
|
<MiniButton
|
||||||
|
title='Сохранить конституенту'
|
||||||
|
noHover
|
||||||
|
onClick={toggleDelete}
|
||||||
|
icon={
|
||||||
|
!deleteRight ? (
|
||||||
|
<IconKeepAliasOn size='1rem' className='clr-text-green' />
|
||||||
|
) : (
|
||||||
|
<IconKeepAliasOff size='1rem' className='clr-text-red' />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<MiniButton
|
||||||
|
title='Сохранить термин'
|
||||||
|
noHover
|
||||||
|
onClick={toggleTerm}
|
||||||
|
icon={
|
||||||
|
!takeLeftTerm ? (
|
||||||
|
<IconKeepTermOn size='1rem' className='clr-text-green' />
|
||||||
|
) : (
|
||||||
|
<IconKeepTermOff size='1rem' className='clr-text-red' />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<SelectConstituenta
|
||||||
|
items={schema2?.items.filter(cst => !filter2 || filter2(cst))}
|
||||||
|
value={rightCst}
|
||||||
|
onSelectValue={setRightCst}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<DataTable
|
||||||
|
dense
|
||||||
|
noHeader
|
||||||
|
noFooter
|
||||||
|
className='w-full text-sm border select-none cc-scroll-y'
|
||||||
|
rows={rows}
|
||||||
|
contentHeight='1.3rem'
|
||||||
|
data={items}
|
||||||
|
columns={columns}
|
||||||
|
headPosition='0'
|
||||||
|
noDataComponent={
|
||||||
|
<NoData className='min-h-[2rem]'>
|
||||||
|
<p>Список пуст</p>
|
||||||
|
<p>Добавьте отождествление</p>
|
||||||
|
</NoData>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PickInlineSubstitutions;
|
|
@ -5,74 +5,100 @@ import { useCallback, useMemo, useState } from 'react';
|
||||||
import BadgeConstituenta from '@/components/info/BadgeConstituenta';
|
import BadgeConstituenta from '@/components/info/BadgeConstituenta';
|
||||||
import SelectConstituenta from '@/components/select/SelectConstituenta';
|
import SelectConstituenta from '@/components/select/SelectConstituenta';
|
||||||
import DataTable, { createColumnHelper } from '@/components/ui/DataTable';
|
import DataTable, { createColumnHelper } from '@/components/ui/DataTable';
|
||||||
import Label from '@/components/ui/Label';
|
|
||||||
import MiniButton from '@/components/ui/MiniButton';
|
import MiniButton from '@/components/ui/MiniButton';
|
||||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
||||||
import { IConstituenta, IRSForm, ISingleSubstitution } from '@/models/rsform';
|
import { LibraryItemID } from '@/models/library';
|
||||||
import { describeConstituenta } from '@/utils/labels';
|
import { ICstSubstitute, IMultiSubstitution, IOperation } from '@/models/oss';
|
||||||
|
import { ConstituentaID, IConstituenta, IRSForm } from '@/models/rsform';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
IconKeepAliasOff,
|
IconKeepAliasOff,
|
||||||
IconKeepAliasOn,
|
IconKeepAliasOn,
|
||||||
IconKeepTermOff,
|
IconKeepTermOff,
|
||||||
IconKeepTermOn,
|
IconKeepTermOn,
|
||||||
IconPageFirst,
|
|
||||||
IconPageLast,
|
IconPageLast,
|
||||||
IconPageLeft,
|
|
||||||
IconPageRight,
|
IconPageRight,
|
||||||
IconRemove,
|
IconRemove,
|
||||||
IconReplace
|
IconReplace
|
||||||
} from '../Icons';
|
} from '../Icons';
|
||||||
import NoData from '../ui/NoData';
|
import NoData from '../ui/NoData';
|
||||||
|
import SelectOperation from './SelectOperation';
|
||||||
|
|
||||||
|
function SubstitutionIcon({ item, className }: { item: IMultiSubstitution; className?: string }) {
|
||||||
|
if (!item.transfer_term) {
|
||||||
|
return <IconPageRight size='1.2rem' className={className} />;
|
||||||
|
} else {
|
||||||
|
return <IconPageLast size='1.2rem' className={className} />;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
interface PickSubstitutionsProps {
|
interface PickSubstitutionsProps {
|
||||||
prefixID: string;
|
prefixID: string;
|
||||||
rows?: number;
|
rows?: number;
|
||||||
|
|
||||||
schema1?: IRSForm;
|
operations: IOperation[];
|
||||||
schema2?: IRSForm;
|
getSchema: (id: LibraryItemID) => IRSForm | undefined;
|
||||||
filter1?: (cst: IConstituenta) => boolean;
|
getConstituenta: (id: ConstituentaID) => IConstituenta | undefined;
|
||||||
filter2?: (cst: IConstituenta) => boolean;
|
getSchemaByCst: (id: ConstituentaID) => IRSForm | undefined;
|
||||||
|
substitutions: ICstSubstitute[];
|
||||||
items: ISingleSubstitution[];
|
setSubstitutions: React.Dispatch<React.SetStateAction<ICstSubstitute[]>>;
|
||||||
setItems: React.Dispatch<React.SetStateAction<ISingleSubstitution[]>>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function SubstitutionIcon({ item }: { item: ISingleSubstitution }) {
|
const columnHelper = createColumnHelper<IMultiSubstitution>();
|
||||||
if (item.deleteRight) {
|
|
||||||
if (item.takeLeftTerm) {
|
|
||||||
return <IconPageRight size='1.2rem' />;
|
|
||||||
} else {
|
|
||||||
return <IconPageLast size='1.2rem' />;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (item.takeLeftTerm) {
|
|
||||||
return <IconPageFirst size='1.2rem' />;
|
|
||||||
} else {
|
|
||||||
return <IconPageLeft size='1.2rem' />;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const columnHelper = createColumnHelper<ISingleSubstitution>();
|
|
||||||
|
|
||||||
function PickSubstitutions({
|
function PickSubstitutions({
|
||||||
items,
|
prefixID,
|
||||||
schema1,
|
|
||||||
schema2,
|
|
||||||
filter1,
|
|
||||||
filter2,
|
|
||||||
rows,
|
rows,
|
||||||
setItems,
|
operations,
|
||||||
prefixID
|
getSchema,
|
||||||
|
getConstituenta,
|
||||||
|
getSchemaByCst,
|
||||||
|
substitutions,
|
||||||
|
setSubstitutions
|
||||||
}: PickSubstitutionsProps) {
|
}: PickSubstitutionsProps) {
|
||||||
const { colors } = useConceptOptions();
|
const { colors } = useConceptOptions();
|
||||||
|
|
||||||
|
const [leftArgument, setLeftArgument] = useState<IOperation | undefined>(undefined);
|
||||||
|
const [rightArgument, setRightArgument] = useState<IOperation | undefined>(undefined);
|
||||||
|
const leftSchema = useMemo(
|
||||||
|
() => (leftArgument?.result ? getSchema(leftArgument.result) : undefined),
|
||||||
|
[leftArgument, getSchema]
|
||||||
|
);
|
||||||
|
|
||||||
|
const rightSchema = useMemo(
|
||||||
|
() => (rightArgument?.result ? getSchema(rightArgument.result) : undefined),
|
||||||
|
[rightArgument, getSchema]
|
||||||
|
);
|
||||||
const [leftCst, setLeftCst] = useState<IConstituenta | undefined>(undefined);
|
const [leftCst, setLeftCst] = useState<IConstituenta | undefined>(undefined);
|
||||||
const [rightCst, setRightCst] = useState<IConstituenta | undefined>(undefined);
|
const [rightCst, setRightCst] = useState<IConstituenta | undefined>(undefined);
|
||||||
|
|
||||||
const [deleteRight, setDeleteRight] = useState(true);
|
const [deleteRight, setDeleteRight] = useState(true);
|
||||||
const [takeLeftTerm, setTakeLeftTerm] = useState(true);
|
const [takeLeftTerm, setTakeLeftTerm] = useState(true);
|
||||||
|
|
||||||
|
const operationByConstituenta = useCallback(
|
||||||
|
(cst: ConstituentaID): IOperation | undefined => {
|
||||||
|
const schema = getSchemaByCst(cst);
|
||||||
|
if (!schema) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const cstOperations = operations.filter(item => item.result === schema.id);
|
||||||
|
return cstOperations.length === 1 ? cstOperations[0] : undefined;
|
||||||
|
},
|
||||||
|
[getSchemaByCst, operations]
|
||||||
|
);
|
||||||
|
|
||||||
|
const substitutionData: IMultiSubstitution[] = useMemo(
|
||||||
|
() =>
|
||||||
|
substitutions.map(item => ({
|
||||||
|
original_operation: operationByConstituenta(item.original),
|
||||||
|
original: getConstituenta(item.original),
|
||||||
|
substitution: getConstituenta(item.substitution),
|
||||||
|
substitution_operation: operationByConstituenta(item.substitution),
|
||||||
|
transfer_term: item.transfer_term
|
||||||
|
})),
|
||||||
|
[getConstituenta, operationByConstituenta, substitutions]
|
||||||
|
);
|
||||||
|
|
||||||
const toggleDelete = () => setDeleteRight(prev => !prev);
|
const toggleDelete = () => setDeleteRight(prev => !prev);
|
||||||
const toggleTerm = () => setTakeLeftTerm(prev => !prev);
|
const toggleTerm = () => setTakeLeftTerm(prev => !prev);
|
||||||
|
|
||||||
|
@ -80,26 +106,20 @@ function PickSubstitutions({
|
||||||
if (!leftCst || !rightCst) {
|
if (!leftCst || !rightCst) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const newSubstitution: ISingleSubstitution = {
|
const newSubstitution: ICstSubstitute = {
|
||||||
leftCst: leftCst,
|
original: deleteRight ? rightCst.id : leftCst.id,
|
||||||
rightCst: rightCst,
|
substitution: deleteRight ? leftCst.id : rightCst.id,
|
||||||
deleteRight: deleteRight,
|
transfer_term: deleteRight != takeLeftTerm
|
||||||
takeLeftTerm: takeLeftTerm
|
|
||||||
};
|
};
|
||||||
setItems([
|
setSubstitutions(prev => [...prev, newSubstitution]);
|
||||||
newSubstitution,
|
setLeftCst(undefined);
|
||||||
...items.filter(
|
setRightCst(undefined);
|
||||||
item =>
|
|
||||||
(!item.deleteRight && item.leftCst.id !== leftCst.id) ||
|
|
||||||
(item.deleteRight && item.rightCst.id !== rightCst.id)
|
|
||||||
)
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleDeleteRow = useCallback(
|
const handleDeleteRow = useCallback(
|
||||||
(row: number) => {
|
(row: number) => {
|
||||||
setItems(prev => {
|
setSubstitutions(prev => {
|
||||||
const newItems: ISingleSubstitution[] = [];
|
const newItems: ICstSubstitute[] = [];
|
||||||
prev.forEach((item, index) => {
|
prev.forEach((item, index) => {
|
||||||
if (index !== row) {
|
if (index !== row) {
|
||||||
newItems.push(item);
|
newItems.push(item);
|
||||||
|
@ -108,23 +128,26 @@ function PickSubstitutions({
|
||||||
return newItems;
|
return newItems;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[setItems]
|
[setSubstitutions]
|
||||||
);
|
);
|
||||||
|
|
||||||
const columns = useMemo(
|
const columns = useMemo(
|
||||||
() => [
|
() => [
|
||||||
columnHelper.accessor(item => describeConstituenta(item.leftCst), {
|
columnHelper.accessor(item => item.substitution_operation?.alias ?? 'N/A', {
|
||||||
id: 'left_text',
|
id: 'left_schema',
|
||||||
header: 'Описание',
|
header: 'Операция',
|
||||||
size: 1000,
|
size: 100,
|
||||||
cell: props => <div className='text-xs text-ellipsis'>{props.getValue()}</div>
|
cell: props => <div className='min-w-[10rem] text-ellipsis text-right'>{props.getValue()}</div>
|
||||||
}),
|
}),
|
||||||
columnHelper.accessor(item => item.leftCst.alias, {
|
columnHelper.accessor(item => item.substitution?.alias ?? 'N/A', {
|
||||||
id: 'left_alias',
|
id: 'left_alias',
|
||||||
header: () => <span className='pl-3'>Имя</span>,
|
header: () => <span className='pl-3'>Имя</span>,
|
||||||
size: 65,
|
size: 65,
|
||||||
cell: props => (
|
cell: props =>
|
||||||
<BadgeConstituenta theme={colors} value={props.row.original.leftCst} prefixID={`${prefixID}_1_`} />
|
props.row.original.substitution ? (
|
||||||
|
<BadgeConstituenta theme={colors} value={props.row.original.substitution} prefixID={`${prefixID}_1_`} />
|
||||||
|
) : (
|
||||||
|
'N/A'
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
columnHelper.display({
|
columnHelper.display({
|
||||||
|
@ -133,29 +156,34 @@ function PickSubstitutions({
|
||||||
size: 40,
|
size: 40,
|
||||||
cell: props => <SubstitutionIcon item={props.row.original} />
|
cell: props => <SubstitutionIcon item={props.row.original} />
|
||||||
}),
|
}),
|
||||||
columnHelper.accessor(item => item.rightCst.alias, {
|
columnHelper.accessor(item => item.original?.alias ?? 'N/A', {
|
||||||
id: 'right_alias',
|
id: 'right_alias',
|
||||||
header: () => <span className='pl-3'>Имя</span>,
|
header: () => <span className='pl-3'>Имя</span>,
|
||||||
size: 65,
|
size: 65,
|
||||||
cell: props => (
|
cell: props =>
|
||||||
<BadgeConstituenta theme={colors} value={props.row.original.rightCst} prefixID={`${prefixID}_2_`} />
|
props.row.original.original ? (
|
||||||
|
<BadgeConstituenta theme={colors} value={props.row.original.original} prefixID={`${prefixID}_1_`} />
|
||||||
|
) : (
|
||||||
|
'N/A'
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
columnHelper.accessor(item => describeConstituenta(item.rightCst), {
|
columnHelper.accessor(item => item.original_operation?.alias ?? 'N/A', {
|
||||||
id: 'right_text',
|
id: 'right_schema',
|
||||||
header: 'Описание',
|
header: 'Операция',
|
||||||
minSize: 1000,
|
size: 100,
|
||||||
cell: props => <div className='text-xs text-ellipsis text-pretty'>{props.getValue()}</div>
|
cell: props => <div className='min-w-[8rem] text-ellipsis'>{props.getValue()}</div>
|
||||||
}),
|
}),
|
||||||
columnHelper.display({
|
columnHelper.display({
|
||||||
id: 'actions',
|
id: 'actions',
|
||||||
cell: props => (
|
cell: props => (
|
||||||
|
<div className='max-w-fit'>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
noHover
|
noHover
|
||||||
title='Удалить'
|
title='Удалить'
|
||||||
icon={<IconRemove size='1rem' className='icon-red' />}
|
icon={<IconRemove size='1rem' className='icon-red' />}
|
||||||
onClick={() => handleDeleteRow(props.row.index)}
|
onClick={() => handleDeleteRow(props.row.index)}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
],
|
],
|
||||||
|
@ -165,10 +193,8 @@ function PickSubstitutions({
|
||||||
return (
|
return (
|
||||||
<div className='flex flex-col w-full'>
|
<div className='flex flex-col w-full'>
|
||||||
<div className='flex items-end gap-3 justify-stretch'>
|
<div className='flex items-end gap-3 justify-stretch'>
|
||||||
<div className='flex-grow basis-1/2'>
|
<div className='flex-grow flex flex-col basis-1/2'>
|
||||||
<div className='flex items-center justify-between'>
|
<div className='cc-icons mb-1 w-fit mx-auto'>
|
||||||
<Label text={schema1 !== schema2 ? schema1?.alias ?? 'Схема 1' : ''} />
|
|
||||||
<div className='cc-icons'>
|
|
||||||
<MiniButton
|
<MiniButton
|
||||||
title='Сохранить конституенту'
|
title='Сохранить конституенту'
|
||||||
noHover
|
noHover
|
||||||
|
@ -194,13 +220,21 @@ function PickSubstitutions({
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div className='flex flex-col gap-[0.125rem] border-x border-t clr-input'>
|
||||||
|
<SelectOperation
|
||||||
|
noBorder
|
||||||
|
items={operations.filter(item => item.id !== rightArgument?.id)}
|
||||||
|
value={leftArgument}
|
||||||
|
onSelectValue={setLeftArgument}
|
||||||
|
/>
|
||||||
<SelectConstituenta
|
<SelectConstituenta
|
||||||
items={schema1?.items.filter(cst => !filter1 || filter1(cst))}
|
noBorder
|
||||||
|
items={leftSchema?.items.filter(cst => !substitutions.find(item => item.original === cst.id))}
|
||||||
value={leftCst}
|
value={leftCst}
|
||||||
onSelectValue={setLeftCst}
|
onSelectValue={setLeftCst}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<MiniButton
|
<MiniButton
|
||||||
noHover
|
noHover
|
||||||
|
@ -212,9 +246,7 @@ function PickSubstitutions({
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className='flex-grow basis-1/2'>
|
<div className='flex-grow basis-1/2'>
|
||||||
<div className='flex items-center justify-between'>
|
<div className='cc-icons mb-1 w-fit mx-auto'>
|
||||||
<Label text={schema1 !== schema2 ? schema2?.alias ?? 'Схема 2' : ''} />
|
|
||||||
<div className='cc-icons'>
|
|
||||||
<MiniButton
|
<MiniButton
|
||||||
title='Сохранить конституенту'
|
title='Сохранить конституенту'
|
||||||
noHover
|
noHover
|
||||||
|
@ -240,14 +272,22 @@ function PickSubstitutions({
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div className='flex flex-col gap-[0.125rem] border-x border-t clr-input'>
|
||||||
|
<SelectOperation
|
||||||
|
noBorder
|
||||||
|
items={operations.filter(item => item.id !== leftArgument?.id)}
|
||||||
|
value={rightArgument}
|
||||||
|
onSelectValue={setRightArgument}
|
||||||
|
/>
|
||||||
<SelectConstituenta
|
<SelectConstituenta
|
||||||
items={schema2?.items.filter(cst => !filter2 || filter2(cst))}
|
noBorder
|
||||||
|
items={rightSchema?.items.filter(cst => !substitutions.find(item => item.original === cst.id))}
|
||||||
value={rightCst}
|
value={rightCst}
|
||||||
onSelectValue={setRightCst}
|
onSelectValue={setRightCst}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<DataTable
|
<DataTable
|
||||||
dense
|
dense
|
||||||
|
@ -256,7 +296,7 @@ function PickSubstitutions({
|
||||||
className='w-full text-sm border select-none cc-scroll-y'
|
className='w-full text-sm border select-none cc-scroll-y'
|
||||||
rows={rows}
|
rows={rows}
|
||||||
contentHeight='1.3rem'
|
contentHeight='1.3rem'
|
||||||
data={items}
|
data={substitutionData}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
headPosition='0'
|
headPosition='0'
|
||||||
noDataComponent={
|
noDataComponent={
|
||||||
|
|
|
@ -49,7 +49,7 @@ function SelectConstituenta({
|
||||||
<SelectSingle
|
<SelectSingle
|
||||||
className={clsx('text-ellipsis', className)}
|
className={clsx('text-ellipsis', className)}
|
||||||
options={options}
|
options={options}
|
||||||
value={value ? { value: value.id, label: `${value.alias}: ${describeConstituentaTerm(value)}` } : undefined}
|
value={value ? { value: value.id, label: `${value.alias}: ${describeConstituentaTerm(value)}` } : null}
|
||||||
onChange={data => onSelectValue(items?.find(cst => cst.id === data?.value))}
|
onChange={data => onSelectValue(items?.find(cst => cst.id === data?.value))}
|
||||||
// @ts-expect-error: TODO: use type definitions from react-select in filter object
|
// @ts-expect-error: TODO: use type definitions from react-select in filter object
|
||||||
filterOption={filter}
|
filterOption={filter}
|
||||||
|
|
|
@ -47,7 +47,7 @@ function SelectOperation({
|
||||||
<SelectSingle
|
<SelectSingle
|
||||||
className={clsx('text-ellipsis', className)}
|
className={clsx('text-ellipsis', className)}
|
||||||
options={options}
|
options={options}
|
||||||
value={value ? { value: value.id, label: `${value.alias}: ${value.title}` } : undefined}
|
value={value ? { value: value.id, label: `${value.alias}: ${value.title}` } : null}
|
||||||
onChange={data => onSelectValue(items?.find(cst => cst.id === data?.value))}
|
onChange={data => onSelectValue(items?.find(cst => cst.id === data?.value))}
|
||||||
// @ts-expect-error: TODO: use type definitions from react-select in filter object
|
// @ts-expect-error: TODO: use type definitions from react-select in filter object
|
||||||
filterOption={filter}
|
filterOption={filter}
|
||||||
|
|
|
@ -49,7 +49,7 @@ function SelectUser({
|
||||||
<SelectSingle
|
<SelectSingle
|
||||||
className={clsx('text-ellipsis', className)}
|
className={clsx('text-ellipsis', className)}
|
||||||
options={options}
|
options={options}
|
||||||
value={value ? { value: value, label: getUserLabel(value) } : undefined}
|
value={value ? { value: value, label: getUserLabel(value) } : null}
|
||||||
onChange={data => {
|
onChange={data => {
|
||||||
if (data !== null && data.value !== undefined) onSelectValue(data.value);
|
if (data !== null && data.value !== undefined) onSelectValue(data.value);
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -10,8 +10,8 @@ import Overlay from '@/components/ui/Overlay';
|
||||||
import TabLabel from '@/components/ui/TabLabel';
|
import TabLabel from '@/components/ui/TabLabel';
|
||||||
import { useLibrary } from '@/context/LibraryContext';
|
import { useLibrary } from '@/context/LibraryContext';
|
||||||
import { LibraryItemID } from '@/models/library';
|
import { LibraryItemID } from '@/models/library';
|
||||||
import { HelpTopic, Position2D } from '@/models/miscellaneous';
|
import { HelpTopic } from '@/models/miscellaneous';
|
||||||
import { IOperationCreateData, IOperationPosition, IOperationSchema, OperationID, OperationType } from '@/models/oss';
|
import { IOperationCreateData, IOperationSchema, OperationID, OperationType } from '@/models/oss';
|
||||||
import { PARAMETER } from '@/utils/constants';
|
import { PARAMETER } from '@/utils/constants';
|
||||||
import { describeOperationType, labelOperationType } from '@/utils/labels';
|
import { describeOperationType, labelOperationType } from '@/utils/labels';
|
||||||
|
|
||||||
|
@ -21,8 +21,6 @@ import TabSynthesisOperation from './TabSynthesisOperation';
|
||||||
interface DlgCreateOperationProps {
|
interface DlgCreateOperationProps {
|
||||||
hideWindow: () => void;
|
hideWindow: () => void;
|
||||||
oss: IOperationSchema;
|
oss: IOperationSchema;
|
||||||
positions: IOperationPosition[];
|
|
||||||
insertPosition: Position2D;
|
|
||||||
onCreate: (data: IOperationCreateData) => void;
|
onCreate: (data: IOperationCreateData) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,7 +29,7 @@ export enum TabID {
|
||||||
SYNTHESIS = 1
|
SYNTHESIS = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
function DlgCreateOperation({ hideWindow, oss, insertPosition, positions, onCreate }: DlgCreateOperationProps) {
|
function DlgCreateOperation({ hideWindow, oss, onCreate }: DlgCreateOperationProps) {
|
||||||
const library = useLibrary();
|
const library = useLibrary();
|
||||||
const [activeTab, setActiveTab] = useState(TabID.INPUT);
|
const [activeTab, setActiveTab] = useState(TabID.INPUT);
|
||||||
|
|
||||||
|
@ -62,8 +60,8 @@ function DlgCreateOperation({ hideWindow, oss, insertPosition, positions, onCrea
|
||||||
const handleSubmit = () => {
|
const handleSubmit = () => {
|
||||||
const data: IOperationCreateData = {
|
const data: IOperationCreateData = {
|
||||||
item_data: {
|
item_data: {
|
||||||
position_x: insertPosition.x,
|
position_x: 0,
|
||||||
position_y: insertPosition.y,
|
position_y: 0,
|
||||||
alias: alias,
|
alias: alias,
|
||||||
title: title,
|
title: title,
|
||||||
comment: comment,
|
comment: comment,
|
||||||
|
@ -71,7 +69,7 @@ function DlgCreateOperation({ hideWindow, oss, insertPosition, positions, onCrea
|
||||||
operation_type: activeTab === TabID.INPUT ? OperationType.INPUT : OperationType.SYNTHESIS,
|
operation_type: activeTab === TabID.INPUT ? OperationType.INPUT : OperationType.SYNTHESIS,
|
||||||
result: activeTab === TabID.INPUT ? attachedID ?? null : null
|
result: activeTab === TabID.INPUT ? attachedID ?? null : null
|
||||||
},
|
},
|
||||||
positions: positions,
|
positions: [],
|
||||||
arguments: activeTab === TabID.INPUT ? undefined : inputs.length > 0 ? inputs : undefined,
|
arguments: activeTab === TabID.INPUT ? undefined : inputs.length > 0 ? inputs : undefined,
|
||||||
create_schema: createSchema
|
create_schema: createSchema
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,172 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import clsx from 'clsx';
|
||||||
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
|
import { TabList, TabPanel, Tabs } from 'react-tabs';
|
||||||
|
|
||||||
|
import BadgeHelp from '@/components/info/BadgeHelp';
|
||||||
|
import Modal from '@/components/ui/Modal';
|
||||||
|
import Overlay from '@/components/ui/Overlay';
|
||||||
|
import TabLabel from '@/components/ui/TabLabel';
|
||||||
|
import useRSFormCache from '@/hooks/useRSFormCache';
|
||||||
|
import { HelpTopic } from '@/models/miscellaneous';
|
||||||
|
import { ICstSubstitute, IOperation, IOperationSchema, OperationID, OperationType } from '@/models/oss';
|
||||||
|
import { PARAMETER } from '@/utils/constants';
|
||||||
|
|
||||||
|
import TabArguments from './TabArguments';
|
||||||
|
import TabOperation from './TabOperation';
|
||||||
|
import TabSynthesis from './TabSynthesis';
|
||||||
|
|
||||||
|
interface DlgEditOperationProps {
|
||||||
|
hideWindow: () => void;
|
||||||
|
oss: IOperationSchema;
|
||||||
|
target: IOperation;
|
||||||
|
// onSubmit: (data: IOperationEditData) => void;
|
||||||
|
onSubmit: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum TabID {
|
||||||
|
CARD = 0,
|
||||||
|
ARGUMENTS = 1,
|
||||||
|
SUBSTITUTION = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
function DlgEditOperation({ hideWindow, oss, target, onSubmit }: DlgEditOperationProps) {
|
||||||
|
const [activeTab, setActiveTab] = useState(TabID.CARD);
|
||||||
|
|
||||||
|
const [alias, setAlias] = useState(target.alias);
|
||||||
|
const [title, setTitle] = useState(target.title);
|
||||||
|
const [comment, setComment] = useState(target.comment);
|
||||||
|
const [syncText, setSyncText] = useState(true);
|
||||||
|
|
||||||
|
const [inputs, setInputs] = useState<OperationID[]>(oss.graph.expandInputs([target.id]));
|
||||||
|
const inputOperations = useMemo(() => inputs.map(id => oss.operationByID.get(id)!), [inputs, oss.operationByID]);
|
||||||
|
const schemasIDs = useMemo(
|
||||||
|
() => inputOperations.map(operation => operation.result).filter(id => id !== null),
|
||||||
|
[inputOperations]
|
||||||
|
);
|
||||||
|
const [substitutions, setSubstitutions] = useState<ICstSubstitute[]>(oss.substitutions);
|
||||||
|
const cache = useRSFormCache();
|
||||||
|
|
||||||
|
const isValid = useMemo(() => alias !== '', [alias]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
cache.preload(schemasIDs);
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [schemasIDs]);
|
||||||
|
|
||||||
|
const handleSubmit = () => {
|
||||||
|
// const data: IOperationCreateData = {
|
||||||
|
// item_data: {
|
||||||
|
// position_x: insertPosition.x,
|
||||||
|
// position_y: insertPosition.y,
|
||||||
|
// alias: alias,
|
||||||
|
// title: title,
|
||||||
|
// comment: comment,
|
||||||
|
// sync_text: activeTab === TabID.INPUT ? syncText : true,
|
||||||
|
// operation_type: activeTab === TabID.INPUT ? OperationType.INPUT : OperationType.SYNTHESIS,
|
||||||
|
// result: activeTab === TabID.INPUT ? attachedID ?? null : null
|
||||||
|
// },
|
||||||
|
// positions: positions,
|
||||||
|
// arguments: activeTab === TabID.INPUT ? undefined : inputs.length > 0 ? inputs : undefined,
|
||||||
|
// create_schema: createSchema
|
||||||
|
// };
|
||||||
|
onSubmit();
|
||||||
|
};
|
||||||
|
|
||||||
|
const cardPanel = useMemo(
|
||||||
|
() => (
|
||||||
|
<TabPanel>
|
||||||
|
<TabOperation
|
||||||
|
alias={alias}
|
||||||
|
setAlias={setAlias}
|
||||||
|
comment={comment}
|
||||||
|
setComment={setComment}
|
||||||
|
title={title}
|
||||||
|
setTitle={setTitle}
|
||||||
|
syncText={syncText}
|
||||||
|
setSyncText={setSyncText}
|
||||||
|
/>
|
||||||
|
</TabPanel>
|
||||||
|
),
|
||||||
|
[alias, comment, title, syncText]
|
||||||
|
);
|
||||||
|
|
||||||
|
const argumentsPanel = useMemo(
|
||||||
|
() => (
|
||||||
|
<TabPanel>
|
||||||
|
<TabArguments
|
||||||
|
target={target.id} // prettier: split-lines
|
||||||
|
oss={oss}
|
||||||
|
inputs={inputs}
|
||||||
|
setInputs={setInputs}
|
||||||
|
/>
|
||||||
|
</TabPanel>
|
||||||
|
),
|
||||||
|
[oss, target, inputs]
|
||||||
|
);
|
||||||
|
|
||||||
|
const synthesisPanel = useMemo(
|
||||||
|
() => (
|
||||||
|
<TabPanel>
|
||||||
|
<TabSynthesis
|
||||||
|
operations={inputOperations}
|
||||||
|
loading={cache.loading}
|
||||||
|
error={cache.error}
|
||||||
|
getSchema={cache.getSchema}
|
||||||
|
getConstituenta={cache.getConstituenta}
|
||||||
|
getSchemaByCst={cache.getSchemaByCst}
|
||||||
|
substitutions={substitutions}
|
||||||
|
setSubstitutions={setSubstitutions}
|
||||||
|
/>
|
||||||
|
</TabPanel>
|
||||||
|
),
|
||||||
|
[
|
||||||
|
inputOperations,
|
||||||
|
cache.loading,
|
||||||
|
cache.error,
|
||||||
|
cache.getSchema,
|
||||||
|
cache.getConstituenta,
|
||||||
|
substitutions,
|
||||||
|
cache.getSchemaByCst
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
header='Редактирование операции'
|
||||||
|
submitText='Сохранить'
|
||||||
|
hideWindow={hideWindow}
|
||||||
|
canSubmit={isValid}
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
className='w-[40rem] px-6 min-h-[35rem]'
|
||||||
|
>
|
||||||
|
<Overlay position='top-0 right-0'>
|
||||||
|
<BadgeHelp topic={HelpTopic.CC_OSS} className={clsx(PARAMETER.TOOLTIP_WIDTH, 'sm:max-w-[40rem]')} offset={14} />
|
||||||
|
</Overlay>
|
||||||
|
|
||||||
|
<Tabs
|
||||||
|
selectedTabClassName='clr-selected'
|
||||||
|
className='flex flex-col'
|
||||||
|
selectedIndex={activeTab}
|
||||||
|
onSelect={setActiveTab}
|
||||||
|
>
|
||||||
|
<TabList className={clsx('mb-3 self-center', 'flex', 'border divide-x rounded-none')}>
|
||||||
|
<TabLabel title='Текстовые поля' label='Карточка' className='w-[8rem]' />
|
||||||
|
{target.operation_type === OperationType.SYNTHESIS ? (
|
||||||
|
<TabLabel title='Выбор аргументов операции' label='Аргументы' className='w-[8rem]' />
|
||||||
|
) : null}
|
||||||
|
{target.operation_type === OperationType.SYNTHESIS ? (
|
||||||
|
<TabLabel title='Таблица отождествлений' label='Отождествления' className='w-[8rem]' />
|
||||||
|
) : null}
|
||||||
|
</TabList>
|
||||||
|
|
||||||
|
{cardPanel}
|
||||||
|
{target.operation_type === OperationType.SYNTHESIS ? argumentsPanel : null}
|
||||||
|
{target.operation_type === OperationType.SYNTHESIS ? synthesisPanel : null}
|
||||||
|
</Tabs>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DlgEditOperation;
|
|
@ -0,0 +1,35 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
|
import FlexColumn from '@/components/ui/FlexColumn';
|
||||||
|
import Label from '@/components/ui/Label';
|
||||||
|
import AnimateFade from '@/components/wrap/AnimateFade';
|
||||||
|
import { IOperationSchema, OperationID } from '@/models/oss';
|
||||||
|
|
||||||
|
import PickMultiOperation from '../../components/select/PickMultiOperation';
|
||||||
|
|
||||||
|
interface TabArgumentsProps {
|
||||||
|
oss: IOperationSchema;
|
||||||
|
target: OperationID;
|
||||||
|
inputs: OperationID[];
|
||||||
|
setInputs: React.Dispatch<React.SetStateAction<OperationID[]>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function TabArguments({ oss, inputs, target, setInputs }: TabArgumentsProps) {
|
||||||
|
const potentialCycle = useMemo(() => [target, ...oss.graph.expandAllOutputs([target])], [target, oss.graph]);
|
||||||
|
const filtered = useMemo(
|
||||||
|
() => oss.items.filter(item => !potentialCycle.includes(item.id)),
|
||||||
|
[oss.items, potentialCycle]
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<AnimateFade className='cc-column'>
|
||||||
|
<FlexColumn>
|
||||||
|
<Label text={`Выбор аргументов: [ ${inputs.length} ]`} />
|
||||||
|
<PickMultiOperation items={filtered} selected={inputs} setSelected={setInputs} rows={8} />
|
||||||
|
</FlexColumn>
|
||||||
|
</AnimateFade>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TabArguments;
|
|
@ -0,0 +1,69 @@
|
||||||
|
import Checkbox from '@/components/ui/Checkbox';
|
||||||
|
import FlexColumn from '@/components/ui/FlexColumn';
|
||||||
|
import TextArea from '@/components/ui/TextArea';
|
||||||
|
import TextInput from '@/components/ui/TextInput';
|
||||||
|
import AnimateFade from '@/components/wrap/AnimateFade';
|
||||||
|
import { limits, patterns } from '@/utils/constants';
|
||||||
|
|
||||||
|
interface TabOperationProps {
|
||||||
|
alias: string;
|
||||||
|
setAlias: React.Dispatch<React.SetStateAction<string>>;
|
||||||
|
title: string;
|
||||||
|
setTitle: React.Dispatch<React.SetStateAction<string>>;
|
||||||
|
comment: string;
|
||||||
|
setComment: React.Dispatch<React.SetStateAction<string>>;
|
||||||
|
syncText: boolean;
|
||||||
|
setSyncText: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function TabOperation({
|
||||||
|
alias,
|
||||||
|
setAlias,
|
||||||
|
title,
|
||||||
|
setTitle,
|
||||||
|
comment,
|
||||||
|
setComment,
|
||||||
|
syncText,
|
||||||
|
setSyncText
|
||||||
|
}: TabOperationProps) {
|
||||||
|
return (
|
||||||
|
<AnimateFade className='cc-column'>
|
||||||
|
<TextInput
|
||||||
|
id='operation_title'
|
||||||
|
label='Полное название'
|
||||||
|
value={title}
|
||||||
|
onChange={event => setTitle(event.target.value)}
|
||||||
|
/>
|
||||||
|
<div className='flex gap-6'>
|
||||||
|
<FlexColumn>
|
||||||
|
<TextInput
|
||||||
|
id='operation_alias'
|
||||||
|
label='Сокращение'
|
||||||
|
className='w-[14rem]'
|
||||||
|
pattern={patterns.library_alias}
|
||||||
|
title={`не более ${limits.library_alias_len} символов`}
|
||||||
|
value={alias}
|
||||||
|
onChange={event => setAlias(event.target.value)}
|
||||||
|
/>
|
||||||
|
<Checkbox
|
||||||
|
value={syncText}
|
||||||
|
setValue={setSyncText}
|
||||||
|
label='Синхронизировать текст'
|
||||||
|
titleHtml='Загрузить текстовые поля<br/> из концептуальной схемы'
|
||||||
|
/>
|
||||||
|
</FlexColumn>
|
||||||
|
|
||||||
|
<TextArea
|
||||||
|
id='operation_comment'
|
||||||
|
label='Описание'
|
||||||
|
noResize
|
||||||
|
rows={3}
|
||||||
|
value={comment}
|
||||||
|
onChange={event => setComment(event.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</AnimateFade>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TabOperation;
|
|
@ -0,0 +1,47 @@
|
||||||
|
import { ErrorData } from '@/components/info/InfoError';
|
||||||
|
import PickSubstitutions from '@/components/select/PickSubstitutions';
|
||||||
|
import DataLoader from '@/components/wrap/DataLoader';
|
||||||
|
import { LibraryItemID } from '@/models/library';
|
||||||
|
import { ICstSubstitute, IOperation } from '@/models/oss';
|
||||||
|
import { ConstituentaID, IConstituenta, IRSForm } from '@/models/rsform';
|
||||||
|
import { prefixes } from '@/utils/constants';
|
||||||
|
|
||||||
|
interface TabSynthesisProps {
|
||||||
|
loading: boolean;
|
||||||
|
error: ErrorData;
|
||||||
|
|
||||||
|
operations: IOperation[];
|
||||||
|
getSchema: (id: LibraryItemID) => IRSForm | undefined;
|
||||||
|
getConstituenta: (id: ConstituentaID) => IConstituenta | undefined;
|
||||||
|
getSchemaByCst: (id: ConstituentaID) => IRSForm | undefined;
|
||||||
|
substitutions: ICstSubstitute[];
|
||||||
|
setSubstitutions: React.Dispatch<React.SetStateAction<ICstSubstitute[]>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function TabSynthesis({
|
||||||
|
operations,
|
||||||
|
loading,
|
||||||
|
error,
|
||||||
|
getSchema,
|
||||||
|
getConstituenta,
|
||||||
|
getSchemaByCst,
|
||||||
|
substitutions,
|
||||||
|
setSubstitutions
|
||||||
|
}: TabSynthesisProps) {
|
||||||
|
return (
|
||||||
|
<DataLoader id='dlg-synthesis-tab' className='cc-column' isLoading={loading} error={error}>
|
||||||
|
<PickSubstitutions
|
||||||
|
prefixID={prefixes.dlg_cst_substitutes_list}
|
||||||
|
rows={8}
|
||||||
|
operations={operations}
|
||||||
|
getSchema={getSchema}
|
||||||
|
getConstituenta={getConstituenta}
|
||||||
|
getSchemaByCst={getSchemaByCst}
|
||||||
|
substitutions={substitutions}
|
||||||
|
setSubstitutions={setSubstitutions}
|
||||||
|
/>
|
||||||
|
</DataLoader>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TabSynthesis;
|
|
@ -0,0 +1 @@
|
||||||
|
export { default } from './DlgEditOperation';
|
|
@ -8,7 +8,7 @@ import Modal, { ModalProps } from '@/components/ui/Modal';
|
||||||
import TabLabel from '@/components/ui/TabLabel';
|
import TabLabel from '@/components/ui/TabLabel';
|
||||||
import useRSFormDetails from '@/hooks/useRSFormDetails';
|
import useRSFormDetails from '@/hooks/useRSFormDetails';
|
||||||
import { LibraryItemID } from '@/models/library';
|
import { LibraryItemID } from '@/models/library';
|
||||||
import { IInlineSynthesisData, IRSForm, ISingleSubstitution } from '@/models/rsform';
|
import { IBinarySubstitution, IInlineSynthesisData, IRSForm } from '@/models/rsform';
|
||||||
|
|
||||||
import TabConstituents from './TabConstituents';
|
import TabConstituents from './TabConstituents';
|
||||||
import TabSchema from './TabSchema';
|
import TabSchema from './TabSchema';
|
||||||
|
@ -30,7 +30,7 @@ function DlgInlineSynthesis({ hideWindow, receiver, onInlineSynthesis }: DlgInli
|
||||||
|
|
||||||
const [donorID, setDonorID] = useState<LibraryItemID | undefined>(undefined);
|
const [donorID, setDonorID] = useState<LibraryItemID | undefined>(undefined);
|
||||||
const [selected, setSelected] = useState<LibraryItemID[]>([]);
|
const [selected, setSelected] = useState<LibraryItemID[]>([]);
|
||||||
const [substitutions, setSubstitutions] = useState<ISingleSubstitution[]>([]);
|
const [substitutions, setSubstitutions] = useState<IBinarySubstitution[]>([]);
|
||||||
|
|
||||||
const source = useRSFormDetails({ target: donorID ? String(donorID) : undefined });
|
const source = useRSFormDetails({ target: donorID ? String(donorID) : undefined });
|
||||||
|
|
||||||
|
|
|
@ -2,10 +2,10 @@
|
||||||
|
|
||||||
import { ErrorData } from '@/components/info/InfoError';
|
import { ErrorData } from '@/components/info/InfoError';
|
||||||
import DataLoader from '@/components/wrap/DataLoader';
|
import DataLoader from '@/components/wrap/DataLoader';
|
||||||
import { ConstituentaID, IRSForm, ISingleSubstitution } from '@/models/rsform';
|
import { ConstituentaID, IBinarySubstitution, IRSForm } from '@/models/rsform';
|
||||||
import { prefixes } from '@/utils/constants';
|
import { prefixes } from '@/utils/constants';
|
||||||
|
|
||||||
import PickSubstitutions from '../../components/select/PickSubstitutions';
|
import PickInlineSubstitutions from '../../components/select/PickInlineSubstitutions';
|
||||||
|
|
||||||
interface TabSubstitutionsProps {
|
interface TabSubstitutionsProps {
|
||||||
receiver?: IRSForm;
|
receiver?: IRSForm;
|
||||||
|
@ -15,8 +15,8 @@ interface TabSubstitutionsProps {
|
||||||
loading?: boolean;
|
loading?: boolean;
|
||||||
error?: ErrorData;
|
error?: ErrorData;
|
||||||
|
|
||||||
substitutions: ISingleSubstitution[];
|
substitutions: IBinarySubstitution[];
|
||||||
setSubstitutions: React.Dispatch<React.SetStateAction<ISingleSubstitution[]>>;
|
setSubstitutions: React.Dispatch<React.SetStateAction<IBinarySubstitution[]>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function TabSubstitutions({
|
function TabSubstitutions({
|
||||||
|
@ -32,7 +32,7 @@ function TabSubstitutions({
|
||||||
}: TabSubstitutionsProps) {
|
}: TabSubstitutionsProps) {
|
||||||
return (
|
return (
|
||||||
<DataLoader id='dlg-substitutions-tab' className='cc-column' isLoading={loading} error={error} hasNoData={!source}>
|
<DataLoader id='dlg-substitutions-tab' className='cc-column' isLoading={loading} error={error} hasNoData={!source}>
|
||||||
<PickSubstitutions
|
<PickInlineSubstitutions
|
||||||
items={substitutions}
|
items={substitutions}
|
||||||
setItems={setSubstitutions}
|
setItems={setSubstitutions}
|
||||||
rows={10}
|
rows={10}
|
||||||
|
|
|
@ -3,11 +3,11 @@
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { useMemo, useState } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
|
|
||||||
import PickSubstitutions from '@/components/select/PickSubstitutions';
|
import PickInlineSubstitutions from '@/components/select/PickInlineSubstitutions';
|
||||||
import Modal, { ModalProps } from '@/components/ui/Modal';
|
import Modal, { ModalProps } from '@/components/ui/Modal';
|
||||||
import { useRSForm } from '@/context/RSFormContext';
|
import { useRSForm } from '@/context/RSFormContext';
|
||||||
import { ICstSubstituteData } from '@/models/oss';
|
import { ICstSubstituteData } from '@/models/oss';
|
||||||
import { ISingleSubstitution } from '@/models/rsform';
|
import { IBinarySubstitution } from '@/models/rsform';
|
||||||
import { prefixes } from '@/utils/constants';
|
import { prefixes } from '@/utils/constants';
|
||||||
|
|
||||||
interface DlgSubstituteCstProps extends Pick<ModalProps, 'hideWindow'> {
|
interface DlgSubstituteCstProps extends Pick<ModalProps, 'hideWindow'> {
|
||||||
|
@ -17,7 +17,7 @@ interface DlgSubstituteCstProps extends Pick<ModalProps, 'hideWindow'> {
|
||||||
function DlgSubstituteCst({ hideWindow, onSubstitute }: DlgSubstituteCstProps) {
|
function DlgSubstituteCst({ hideWindow, onSubstitute }: DlgSubstituteCstProps) {
|
||||||
const { schema } = useRSForm();
|
const { schema } = useRSForm();
|
||||||
|
|
||||||
const [substitutions, setSubstitutions] = useState<ISingleSubstitution[]>([]);
|
const [substitutions, setSubstitutions] = useState<IBinarySubstitution[]>([]);
|
||||||
|
|
||||||
const canSubmit = useMemo(() => substitutions.length > 0, [substitutions]);
|
const canSubmit = useMemo(() => substitutions.length > 0, [substitutions]);
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ function DlgSubstituteCst({ hideWindow, onSubstitute }: DlgSubstituteCstProps) {
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
className={clsx('w-[40rem]', 'px-6 pb-3')}
|
className={clsx('w-[40rem]', 'px-6 pb-3')}
|
||||||
>
|
>
|
||||||
<PickSubstitutions
|
<PickInlineSubstitutions
|
||||||
items={substitutions}
|
items={substitutions}
|
||||||
setItems={setSubstitutions}
|
setItems={setSubstitutions}
|
||||||
rows={6}
|
rows={6}
|
||||||
|
|
82
rsconcept/frontend/src/hooks/useRSFormCache.ts
Normal file
82
rsconcept/frontend/src/hooks/useRSFormCache.ts
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
|
import { getRSFormDetails } from '@/backend/rsforms';
|
||||||
|
import { type ErrorData } from '@/components/info/InfoError';
|
||||||
|
import { LibraryItemID } from '@/models/library';
|
||||||
|
import { ConstituentaID, IRSForm, IRSFormData } from '@/models/rsform';
|
||||||
|
import { RSFormLoader } from '@/models/RSFormLoader';
|
||||||
|
|
||||||
|
function useRSFormCache() {
|
||||||
|
const [cache, setCache] = useState<IRSForm[]>([]);
|
||||||
|
const [pending, setPending] = useState<LibraryItemID[]>([]);
|
||||||
|
const [processing, setProcessing] = useState<LibraryItemID[]>([]);
|
||||||
|
const loading = useMemo(() => pending.length > 0 || processing.length > 0, [pending, processing]);
|
||||||
|
const [error, setError] = useState<ErrorData>(undefined);
|
||||||
|
|
||||||
|
function setSchema(data: IRSFormData) {
|
||||||
|
const schema = new RSFormLoader(data).produceRSForm();
|
||||||
|
setCache(prev => [...prev, schema]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const getSchema = useCallback((id: LibraryItemID) => cache.find(item => item.id === id), [cache]);
|
||||||
|
|
||||||
|
const getSchemaByCst = useCallback(
|
||||||
|
(id: ConstituentaID) => {
|
||||||
|
for (const schema of cache) {
|
||||||
|
const cst = schema.items.find(cst => cst.id === id);
|
||||||
|
if (cst) {
|
||||||
|
return schema;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
},
|
||||||
|
[cache]
|
||||||
|
);
|
||||||
|
|
||||||
|
const getConstituenta = useCallback(
|
||||||
|
(id: ConstituentaID) => {
|
||||||
|
for (const schema of cache) {
|
||||||
|
const cst = schema.items.find(cst => cst.id === id);
|
||||||
|
if (cst) {
|
||||||
|
return cst;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
},
|
||||||
|
[cache]
|
||||||
|
);
|
||||||
|
|
||||||
|
const preload = useCallback(
|
||||||
|
(target: LibraryItemID[]) => setPending(prev => [...prev, ...target.filter(id => !prev.includes(id))]),
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const ids = pending.filter(id => !processing.includes(id) && !cache.find(schema => schema.id === id));
|
||||||
|
if (ids.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setProcessing(prev => [...prev, ...ids]);
|
||||||
|
setPending([]);
|
||||||
|
ids.forEach(id =>
|
||||||
|
getRSFormDetails(String(id), '', {
|
||||||
|
showError: false,
|
||||||
|
onError: error => {
|
||||||
|
setProcessing(prev => prev.filter(item => item !== id));
|
||||||
|
setError(error);
|
||||||
|
},
|
||||||
|
onSuccess: data => {
|
||||||
|
setProcessing(prev => prev.filter(item => item !== id));
|
||||||
|
setSchema(data);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [pending]);
|
||||||
|
|
||||||
|
return { preload, getSchema, getConstituenta, getSchemaByCst, loading, error, setError };
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useRSFormCache;
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
import { Graph } from './Graph';
|
import { Graph } from './Graph';
|
||||||
import { ILibraryItem, ILibraryItemData, LibraryItemID } from './library';
|
import { ILibraryItem, ILibraryItemData, LibraryItemID } from './library';
|
||||||
import { ConstituentaID } from './rsform';
|
import { ConstituentaID, IConstituenta } from './rsform';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents {@link IOperation} identifier type.
|
* Represents {@link IOperation} identifier type.
|
||||||
|
@ -101,6 +101,17 @@ export interface ICstSubstituteData {
|
||||||
substitutions: ICstSubstitute[];
|
substitutions: ICstSubstitute[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents substitution for multi synthesis table.
|
||||||
|
*/
|
||||||
|
export interface IMultiSubstitution {
|
||||||
|
original_operation: IOperation | undefined;
|
||||||
|
original: IConstituenta | undefined;
|
||||||
|
substitution: IConstituenta | undefined;
|
||||||
|
substitution_operation: IOperation | undefined;
|
||||||
|
transfer_term: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents {@link ICstSubstitute} extended data.
|
* Represents {@link ICstSubstitute} extended data.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -241,9 +241,9 @@ export interface IVersionCreatedResponse {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents single substitution for synthesis table.
|
* Represents single substitution for binary synthesis table.
|
||||||
*/
|
*/
|
||||||
export interface ISingleSubstitution {
|
export interface IBinarySubstitution {
|
||||||
leftCst: IConstituenta;
|
leftCst: IConstituenta;
|
||||||
rightCst: IConstituenta;
|
rightCst: IConstituenta;
|
||||||
deleteRight: boolean;
|
deleteRight: boolean;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
import { IconConnect, IconDestroy, IconEdit2, IconExecute, IconNewItem, IconRSForm } from '@/components/Icons';
|
import { IconConnect, IconDestroy, IconEdit2, IconExecute, IconNewItem, IconRSForm } from '@/components/Icons';
|
||||||
|
@ -24,6 +24,7 @@ interface NodeContextMenuProps extends ContextMenuData {
|
||||||
onDelete: (target: OperationID) => void;
|
onDelete: (target: OperationID) => void;
|
||||||
onCreateInput: (target: OperationID) => void;
|
onCreateInput: (target: OperationID) => void;
|
||||||
onEditSchema: (target: OperationID) => void;
|
onEditSchema: (target: OperationID) => void;
|
||||||
|
onEditOperation: (target: OperationID) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
function NodeContextMenu({
|
function NodeContextMenu({
|
||||||
|
@ -33,11 +34,32 @@ function NodeContextMenu({
|
||||||
onHide,
|
onHide,
|
||||||
onDelete,
|
onDelete,
|
||||||
onCreateInput,
|
onCreateInput,
|
||||||
onEditSchema
|
onEditSchema,
|
||||||
|
onEditOperation
|
||||||
}: NodeContextMenuProps) {
|
}: NodeContextMenuProps) {
|
||||||
const controller = useOssEdit();
|
const controller = useOssEdit();
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const ref = useRef(null);
|
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(() => {
|
const handleHide = useCallback(() => {
|
||||||
setIsOpen(false);
|
setIsOpen(false);
|
||||||
|
@ -58,8 +80,8 @@ function NodeContextMenu({
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleEditOperation = () => {
|
const handleEditOperation = () => {
|
||||||
toast.error('Not implemented');
|
|
||||||
handleHide();
|
handleHide();
|
||||||
|
onEditOperation(operation.id);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDeleteOperation = () => {
|
const handleDeleteOperation = () => {
|
||||||
|
@ -118,9 +140,13 @@ function NodeContextMenu({
|
||||||
{controller.isMutable && !operation.result && operation.operation_type === OperationType.SYNTHESIS ? (
|
{controller.isMutable && !operation.result && operation.operation_type === OperationType.SYNTHESIS ? (
|
||||||
<DropdownButton
|
<DropdownButton
|
||||||
text='Выполнить синтез'
|
text='Выполнить синтез'
|
||||||
title='Выполнить операцию и получить синтезированную КС'
|
title={
|
||||||
|
readyForSynthesis
|
||||||
|
? 'Выполнить операцию и получить синтезированную КС'
|
||||||
|
: 'Необходимо предоставить все аргументы'
|
||||||
|
}
|
||||||
icon={<IconExecute size='1rem' className='icon-green' />}
|
icon={<IconExecute size='1rem' className='icon-green' />}
|
||||||
disabled={controller.isProcessing}
|
disabled={controller.isProcessing || !readyForSynthesis}
|
||||||
onClick={handleRunSynthesis}
|
onClick={handleRunSynthesis}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
|
@ -155,6 +155,13 @@ function OssFlow({ isModified, setIsModified }: OssFlowProps) {
|
||||||
[controller, getPositions]
|
[controller, getPositions]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleEditOperation = useCallback(
|
||||||
|
(target: OperationID) => {
|
||||||
|
controller.promptEditOperation(target, getPositions());
|
||||||
|
},
|
||||||
|
[controller, getPositions]
|
||||||
|
);
|
||||||
|
|
||||||
const handleFitView = useCallback(() => {
|
const handleFitView = useCallback(() => {
|
||||||
flow.fitView({ duration: PARAMETER.zoomDuration });
|
flow.fitView({ duration: PARAMETER.zoomDuration });
|
||||||
}, [flow]);
|
}, [flow]);
|
||||||
|
@ -301,6 +308,7 @@ function OssFlow({ isModified, setIsModified }: OssFlowProps) {
|
||||||
onDelete={handleDeleteOperation}
|
onDelete={handleDeleteOperation}
|
||||||
onCreateInput={handleCreateInput}
|
onCreateInput={handleCreateInput}
|
||||||
onEditSchema={handleEditSchema}
|
onEditSchema={handleEditSchema}
|
||||||
|
onEditOperation={handleEditOperation}
|
||||||
{...menuProps}
|
{...menuProps}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
|
@ -14,6 +14,7 @@ import DlgChangeInputSchema from '@/dialogs/DlgChangeInputSchema';
|
||||||
import DlgChangeLocation from '@/dialogs/DlgChangeLocation';
|
import DlgChangeLocation from '@/dialogs/DlgChangeLocation';
|
||||||
import DlgCreateOperation from '@/dialogs/DlgCreateOperation';
|
import DlgCreateOperation from '@/dialogs/DlgCreateOperation';
|
||||||
import DlgEditEditors from '@/dialogs/DlgEditEditors';
|
import DlgEditEditors from '@/dialogs/DlgEditEditors';
|
||||||
|
import DlgEditOperation from '@/dialogs/DlgEditOperation';
|
||||||
import { AccessPolicy, LibraryItemID } from '@/models/library';
|
import { AccessPolicy, LibraryItemID } from '@/models/library';
|
||||||
import { Position2D } from '@/models/miscellaneous';
|
import { Position2D } from '@/models/miscellaneous';
|
||||||
import {
|
import {
|
||||||
|
@ -53,6 +54,7 @@ export interface IOssEditContext {
|
||||||
deleteOperation: (target: OperationID, positions: IOperationPosition[]) => void;
|
deleteOperation: (target: OperationID, positions: IOperationPosition[]) => void;
|
||||||
createInput: (target: OperationID, positions: IOperationPosition[]) => void;
|
createInput: (target: OperationID, positions: IOperationPosition[]) => void;
|
||||||
promptEditInput: (target: OperationID, positions: IOperationPosition[]) => void;
|
promptEditInput: (target: OperationID, positions: IOperationPosition[]) => void;
|
||||||
|
promptEditOperation: (target: OperationID, positions: IOperationPosition[]) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const OssEditContext = createContext<IOssEditContext | null>(null);
|
const OssEditContext = createContext<IOssEditContext | null>(null);
|
||||||
|
@ -88,6 +90,7 @@ export const OssEditState = ({ selected, setSelected, children }: OssEditStatePr
|
||||||
const [showEditEditors, setShowEditEditors] = useState(false);
|
const [showEditEditors, setShowEditEditors] = useState(false);
|
||||||
const [showEditLocation, setShowEditLocation] = useState(false);
|
const [showEditLocation, setShowEditLocation] = useState(false);
|
||||||
const [showEditInput, setShowEditInput] = useState(false);
|
const [showEditInput, setShowEditInput] = useState(false);
|
||||||
|
const [showEditOperation, setShowEditOperation] = useState(false);
|
||||||
|
|
||||||
const [showCreateOperation, setShowCreateOperation] = useState(false);
|
const [showCreateOperation, setShowCreateOperation] = useState(false);
|
||||||
const [insertPosition, setInsertPosition] = useState<Position2D>({ x: 0, y: 0 });
|
const [insertPosition, setInsertPosition] = useState<Position2D>({ x: 0, y: 0 });
|
||||||
|
@ -211,11 +214,32 @@ export const OssEditState = ({ selected, setSelected, children }: OssEditStatePr
|
||||||
|
|
||||||
const handleCreateOperation = useCallback(
|
const handleCreateOperation = useCallback(
|
||||||
(data: IOperationCreateData) => {
|
(data: IOperationCreateData) => {
|
||||||
|
data.positions = positions;
|
||||||
|
data.item_data.position_x = insertPosition.x;
|
||||||
|
data.item_data.position_y = insertPosition.y;
|
||||||
model.createOperation(data, operation => toast.success(information.newOperation(operation.alias)));
|
model.createOperation(data, operation => toast.success(information.newOperation(operation.alias)));
|
||||||
},
|
},
|
||||||
[model]
|
[model, positions, insertPosition]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const promptEditOperation = useCallback((target: OperationID, positions: IOperationPosition[]) => {
|
||||||
|
setPositions(positions);
|
||||||
|
setTargetOperationID(target);
|
||||||
|
setShowEditOperation(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleEditOperation = useCallback(() => {
|
||||||
|
// TODO: проверить наличие всех аргументов
|
||||||
|
// TODO: проверить наличие синтеза
|
||||||
|
// TODO: проверить полноту синтеза
|
||||||
|
// TODO: проверить правильность синтеза
|
||||||
|
// TODO: сохранить позиции
|
||||||
|
// TODO: обновить схему
|
||||||
|
// model.setInput(data, () => toast.success(information.changesSaved));
|
||||||
|
|
||||||
|
toast.error('Not implemented');
|
||||||
|
}, []);
|
||||||
|
|
||||||
const deleteOperation = useCallback(
|
const deleteOperation = useCallback(
|
||||||
(target: OperationID, positions: IOperationPosition[]) => {
|
(target: OperationID, positions: IOperationPosition[]) => {
|
||||||
model.deleteOperation({ target: target, positions: positions }, () =>
|
model.deleteOperation({ target: target, positions: positions }, () =>
|
||||||
|
@ -283,7 +307,8 @@ export const OssEditState = ({ selected, setSelected, children }: OssEditStatePr
|
||||||
promptCreateOperation,
|
promptCreateOperation,
|
||||||
deleteOperation,
|
deleteOperation,
|
||||||
createInput,
|
createInput,
|
||||||
promptEditInput
|
promptEditInput,
|
||||||
|
promptEditOperation
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{model.schema ? (
|
{model.schema ? (
|
||||||
|
@ -306,8 +331,6 @@ export const OssEditState = ({ selected, setSelected, children }: OssEditStatePr
|
||||||
<DlgCreateOperation
|
<DlgCreateOperation
|
||||||
hideWindow={() => setShowCreateOperation(false)}
|
hideWindow={() => setShowCreateOperation(false)}
|
||||||
oss={model.schema}
|
oss={model.schema}
|
||||||
positions={positions}
|
|
||||||
insertPosition={insertPosition}
|
|
||||||
onCreate={handleCreateOperation}
|
onCreate={handleCreateOperation}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
@ -319,6 +342,14 @@ export const OssEditState = ({ selected, setSelected, children }: OssEditStatePr
|
||||||
onSubmit={setTargetInput}
|
onSubmit={setTargetInput}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
{showEditOperation ? (
|
||||||
|
<DlgEditOperation
|
||||||
|
hideWindow={() => setShowEditOperation(false)}
|
||||||
|
oss={model.schema}
|
||||||
|
target={targetOperation!}
|
||||||
|
onSubmit={handleEditOperation}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user