F: Improve OSS UI

This commit is contained in:
Ivan 2024-08-17 12:16:50 +03:00
parent a97d1bebb9
commit 259259ec7e
13 changed files with 102 additions and 33 deletions

View File

@ -72,6 +72,11 @@ function TooltipOperation({ node, anchor }: TooltipOperationProps) {
<b>КС не принадлежит ОСС</b> <b>КС не принадлежит ОСС</b>
</p> </p>
) : null} ) : null}
{node.data.operation.is_consolidation ? (
<p>
<b>Ромбовидный синтез</b>
</p>
) : null}
{node.data.operation.title ? ( {node.data.operation.title ? (
<p> <p>
<b>Название: </b> <b>Название: </b>

View File

@ -62,6 +62,7 @@ export class OssLoader {
this.graph.topologicalOrder().forEach(operationID => { this.graph.topologicalOrder().forEach(operationID => {
const operation = this.operationByID.get(operationID)!; const operation = this.operationByID.get(operationID)!;
const schema = this.items.find(item => item.id === operation.result); const schema = this.items.find(item => item.id === operation.result);
operation.is_consolidation = this.inferConsolidation(operationID);
operation.is_owned = !schema || (schema.owner === this.oss.owner && schema.location === this.oss.location); operation.is_owned = !schema || (schema.owner === this.oss.owner && schema.location === this.oss.location);
operation.substitutions = this.oss.substitutions.filter(item => item.operation === operationID); operation.substitutions = this.oss.substitutions.filter(item => item.operation === operationID);
operation.arguments = this.oss.arguments operation.arguments = this.oss.arguments
@ -70,6 +71,19 @@ export class OssLoader {
}); });
} }
private inferConsolidation(operationID: OperationID): boolean {
const inputs = this.graph.expandInputs([operationID]);
if (inputs.length === 0) {
return false;
}
const ancestors = [...inputs];
inputs.forEach(input => {
ancestors.push(...this.graph.expandAllInputs([input]));
});
const unique = new Set(ancestors);
return unique.size < ancestors.length;
}
private calculateStats(): IOperationSchemaStats { private calculateStats(): IOperationSchemaStats {
const items = this.oss.items; const items = this.oss.items;
return { return {

View File

@ -37,6 +37,7 @@ export interface IOperation {
result: LibraryItemID | null; result: LibraryItemID | null;
is_owned: boolean; is_owned: boolean;
is_consolidation: boolean; // aka 'diamond synthesis'
substitutions: ICstSubstituteEx[]; substitutions: ICstSubstituteEx[];
arguments: OperationID[]; arguments: OperationID[];
} }

View File

@ -148,7 +148,7 @@ function TableLibraryItems({ items, resetQuery, folderMode, toggleFolderMode }:
{ {
when: (item: ILibraryItem) => item.item_type === LibraryItemType.OSS, when: (item: ILibraryItem) => item.item_type === LibraryItemType.OSS,
style: { style: {
backgroundColor: colors.bgGreen50 color: colors.fgGreen
} }
} }
], ],

View File

@ -1,24 +1,37 @@
import { IconOSS, IconPredecessor } from '@/components/Icons';
import LinkTopic from '@/components/ui/LinkTopic'; import LinkTopic from '@/components/ui/LinkTopic';
import { HelpTopic } from '@/models/miscellaneous'; import { HelpTopic } from '@/models/miscellaneous';
function HelpConceptOSS() { function HelpConceptOSS() {
return ( return (
<div> <div className='text-justify'>
<h1>Операционная схема синтеза</h1> <h1>Операционная схема синтеза</h1>
<p> <p>
Работа со сложными предметными областями требует многократного{' '} Работа со сложными предметными областями требует многократного{' '}
<LinkTopic text='синтеза' topic={HelpTopic.CC_SYNTHESIS} /> для построения целевых понятий. Последовательность <LinkTopic text='синтеза' topic={HelpTopic.CC_SYNTHESIS} /> для построения целевых понятий. Последовательность
синтезов концептуальных схем задается с помощью <b>Операционной схемы синтеза (ОСС)</b> в форме Графа синтеза. синтезов задается с помощью{' '}
<span className='text-nowrap'>
<IconOSS className='inline-icon' /> <b>Операционной схемы синтеза (ОСС)</b>
</span>{' '}
и отображается в форме <LinkTopic text='Графа синтеза' topic={HelpTopic.UI_OSS_GRAPH} />.
</p> </p>
<p> <p>
Отдельные операции в рамках ОСС задаются <b>таблицами отождествлений</b> понятий из синтезируемых схем. Таким Отдельные операции в рамках ОСС задаются <b>таблицами отождествлений</b> понятий из синтезируемых схем. Таким
образом <LinkTopic text='конституенты' topic={HelpTopic.CC_CONSTITUENTA} /> в каждой КС разделяются на образом <LinkTopic text='конституенты' topic={HelpTopic.CC_CONSTITUENTA} /> в каждой КС разделяются на исходные
наследованные, отождествленные и дописанные. (дописанные), наследованные, отождествленные (удаляемые).
</p> </p>
<p> <p>
Портал поддерживает <b>сквозные изменения</b> в рамках ОСС. Изменения, внесенные в исходные концептуальные схемы Портал поддерживает <b>сквозные изменения</b> в рамках ОСС. Изменения, внесенные в исходные концептуальные схемы
автоматически проносятся через граф синтеза (путем обновления наследованных конституент). Формальные определения автоматически проносятся через граф синтеза (путем обновления наследованных конституент). Формальные определения
наследованных конституент можно редактировать только путем изменения исходных конституент. наследованных конституент можно редактировать только путем изменения{' '}
<span className='text-nowrap'>
<IconPredecessor className='inline-icon' /> исходных конституент.
</span>
</p>
<p>
<b>Ромбовидным синтезом</b> называется операция, где используются КС, имеющие общих предков. При таком синтезе
могут возникать дубликаты и неоднозначности в результате. Необходимо внимательно формировать таблицу
отождествлений, добавляя дублирующиеся понятия из синтезируемых схем.
</p> </p>
</div> </div>
); );

View File

@ -40,8 +40,8 @@ function HelpConceptSynthesis() {
<LinkTopic text='разделе Операции' topic={HelpTopic.RSL_OPERATIONS} /> <LinkTopic text='разделе Операции' topic={HelpTopic.RSL_OPERATIONS} />
</p> </p>
<p> <p>
Для управления совокупностью синтезов используются <b>операционные схемы синтеза</b>. В данный момент этот Для управления совокупностью синтезов используются{' '}
функционал еще не реализован в Портале. <LinkTopic text='операционные схемы синтеза' topic={HelpTopic.CC_OSS} />.
</p> </p>
</div> </div>
); );

View File

@ -5,18 +5,28 @@ import {
IconFolderEmpty, IconFolderEmpty,
IconFolderOpened, IconFolderOpened,
IconFolderTree, IconFolderTree,
IconOSS,
IconRSForm,
IconSearch, IconSearch,
IconShow, IconShow,
IconSortAsc, IconSortAsc,
IconSortDesc IconSortDesc
} from '@/components/Icons'; } from '@/components/Icons';
import { useConceptOptions } from '@/context/ConceptOptionsContext';
function HelpLibrary() { function HelpLibrary() {
const { colors } = useConceptOptions();
return ( return (
<div> <div>
<h1>Библиотека схем</h1> <h1>Библиотека схем</h1>
<p>В библиотеке собраны концептуальные схемы, эксплицированные в родоструктурном аппарате</p> <p>
В библиотеке собраны <IconRSForm size='1rem' className='inline-icon' /> системы определений (КС) <br />и
<IconOSS size='1rem' className='inline-icon' /> операционные схемы синтеза (ОСС).
</p>
<li>
<span style={{ color: colors.fgGreen }}>зеленым текстом</span> выделены ОСС
</li>
<li>клик по строке - переход к редактированию схемы</li> <li>клик по строке - переход к редактированию схемы</li>
<li>Ctrl + клик по строке откроет схему в новой вкладке</li> <li>Ctrl + клик по строке откроет схему в новой вкладке</li>
<li>Фильтры атрибутов три позиции: да/нет/не применять</li> <li>Фильтры атрибутов три позиции: да/нет/не применять</li>

View File

@ -1,4 +1,5 @@
import { import {
IconAlert,
IconAnimation, IconAnimation,
IconAnimationOff, IconAnimationOff,
IconConnect, IconConnect,
@ -23,7 +24,7 @@ import { HelpTopic } from '@/models/miscellaneous';
function HelpOssGraph() { function HelpOssGraph() {
return ( return (
<div className='flex flex-col'> <div className='flex flex-col'>
<h1>Граф синтеза</h1> <h1 className='sm:pr-[6rem]'>Граф синтеза</h1>
<div className='flex flex-col sm:flex-row'> <div className='flex flex-col sm:flex-row'>
<div className='w-full sm:w-[14rem]'> <div className='w-full sm:w-[14rem]'>
<h1>Настройка графа</h1> <h1>Настройка графа</h1>
@ -50,7 +51,10 @@ function HelpOssGraph() {
<li>Клик на операцию выделение</li> <li>Клик на операцию выделение</li>
<li>Esc сбросить выделение</li> <li>Esc сбросить выделение</li>
<li> <li>
<IconEdit2 className='inline-icon' /> Двойной клик редактирование Двойной клик переход к связанной <LinkTopic text='КС' topic={HelpTopic.CC_SYSTEM} />
</li>
<li>
<IconEdit2 className='inline-icon' /> Редактирование операции
</li> </li>
<li> <li>
<IconNewItem className='inline-icon icon-green' /> Новая операция <IconNewItem className='inline-icon icon-green' /> Новая операция
@ -73,7 +77,7 @@ function HelpOssGraph() {
<IconSave className='inline-icon' /> Сохранить положения <IconSave className='inline-icon' /> Сохранить положения
</li> </li>
<li> <li>
<IconImage className='inline-icon' /> Сохранить в формат SVG <IconImage className='inline-icon' /> Сохранить в SVG
</li> </li>
</div> </div>
@ -82,9 +86,12 @@ function HelpOssGraph() {
<div className='dense w-[21rem]'> <div className='dense w-[21rem]'>
<h1>Контекстное меню</h1> <h1>Контекстное меню</h1>
<li> <li>
<IconRSForm className='inline-icon icon-green' /> Переход к связанной{' '} <IconRSForm className='inline-icon icon-green' /> Статус связанной{' '}
<LinkTopic text='КС' topic={HelpTopic.CC_SYSTEM} /> <LinkTopic text='КС' topic={HelpTopic.CC_SYSTEM} />
</li> </li>
<li>
<IconAlert className='inline-icon' /> <LinkTopic text='Ромбовидный синтез' topic={HelpTopic.CC_OSS} />
</li>
<li> <li>
<IconNewRSForm className='inline-icon icon-green' /> Создать пустую КС для загрузки <IconNewRSForm className='inline-icon icon-green' /> Создать пустую КС для загрузки
</li> </li>

View File

@ -21,11 +21,12 @@ function InputNode(node: OssNodeInternal) {
<> <>
<Handle type='source' position={Position.Bottom} /> <Handle type='source' position={Position.Bottom} />
<Overlay position='top-[-0.2rem] right-[-0.2rem]'> <Overlay position='top-0 right-0' className='flex'>
<MiniButton <MiniButton
icon={<IconRSForm className={hasFile ? 'clr-text-green' : 'clr-text-red'} size='0.75rem' />} icon={<IconRSForm className={hasFile ? 'clr-text-green' : 'clr-text-red'} size='0.75rem' />}
noHover noHover
title='Связанная КС' noPadding
title={hasFile ? 'Связанная КС' : 'Нет связанной КС'}
hideTitle={!controller.showTooltip} hideTitle={!controller.showTooltip}
onClick={() => { onClick={() => {
handleOpenSchema(); handleOpenSchema();

View File

@ -105,7 +105,7 @@ function NodeContextMenu({
<Dropdown isOpen={isOpen} stretchLeft={cursorX >= window.innerWidth - PARAMETER.ossContextMenuWidth}> <Dropdown isOpen={isOpen} stretchLeft={cursorX >= window.innerWidth - PARAMETER.ossContextMenuWidth}>
<DropdownButton <DropdownButton
text='Редактировать' text='Редактировать'
titleHtml={prepareTooltip('Редактировать операцию', 'Двойной клик')} title='Редактировать операцию'
icon={<IconEdit2 size='1rem' className='icon-primary' />} icon={<IconEdit2 size='1rem' className='icon-primary' />}
disabled={controller.isProcessing} disabled={controller.isProcessing}
onClick={handleEditOperation} onClick={handleEditOperation}
@ -113,7 +113,7 @@ function NodeContextMenu({
{operation.result ? ( {operation.result ? (
<DropdownButton <DropdownButton
text='Открыть схему' text={prepareTooltip('Открыть схему', 'Двойной клик')}
title='Открыть привязанную КС' title='Открыть привязанную КС'
icon={<IconRSForm size='1rem' className='icon-green' />} icon={<IconRSForm size='1rem' className='icon-green' />}
disabled={controller.isProcessing} disabled={controller.isProcessing}

View File

@ -1,6 +1,6 @@
import { Handle, Position } from 'reactflow'; import { Handle, Position } from 'reactflow';
import { IconRSForm } from '@/components/Icons'; import { IconAlert, IconRSForm } from '@/components/Icons';
import TooltipOperation from '@/components/info/TooltipOperation'; import TooltipOperation from '@/components/info/TooltipOperation';
import MiniButton from '@/components/ui/MiniButton.tsx'; import MiniButton from '@/components/ui/MiniButton.tsx';
import Overlay from '@/components/ui/Overlay'; import Overlay from '@/components/ui/Overlay';
@ -22,15 +22,31 @@ function OperationNode(node: OssNodeInternal) {
<> <>
<Handle type='source' position={Position.Bottom} /> <Handle type='source' position={Position.Bottom} />
<Overlay position='top-[-0.2rem] right-[-0.2rem]'> <Overlay position='top-0 right-0' className='flex flex-col gap-1'>
<MiniButton <MiniButton
icon={<IconRSForm className={hasFile ? 'clr-text-green' : 'clr-text-red'} size='0.75rem' />} icon={
<IconRSForm
className={hasFile ? 'clr-text-green' : 'clr-text-red'}
size={node.data.operation.is_consolidation ? '0.6rem' : '0.75rem'}
/>
}
noHover noHover
title='Связанная КС' noPadding
title={hasFile ? 'Связанная КС' : 'Нет связанной КС'}
hideTitle={!controller.showTooltip} hideTitle={!controller.showTooltip}
onClick={handleOpenSchema} onClick={handleOpenSchema}
disabled={!hasFile} disabled={!hasFile}
/> />
{node.data.operation.is_consolidation ? (
<MiniButton
icon={<IconAlert className='clr-text-primary' size='0.6rem' />}
disabled
noPadding
noHover
title='Внимание! Ромбовидный синтез'
hideTitle={!controller.showTooltip}
/>
) : null}
</Overlay> </Overlay>
{!node.data.operation.is_owned ? ( {!node.data.operation.is_owned ? (

View File

@ -299,9 +299,13 @@ function OssFlow({ isModified, setIsModified }: OssFlowProps) {
(event: CProps.EventMouse, node: OssNode) => { (event: CProps.EventMouse, node: OssNode) => {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
if (node.data.operation.result) {
controller.openOperationSchema(Number(node.id));
} else {
handleEditOperation(Number(node.id)); handleEditOperation(Number(node.id));
}
}, },
[handleEditOperation] [handleEditOperation, controller.openOperationSchema]
); );
function handleKeyDown(event: React.KeyboardEvent<HTMLDivElement>) { function handleKeyDown(event: React.KeyboardEvent<HTMLDivElement>) {

View File

@ -89,8 +89,7 @@ function EditorLibraryItem({ item, isModified, controller }: EditorLibraryItemPr
/> />
{accessLevel >= UserLevel.OWNER ? ( {accessLevel >= UserLevel.OWNER ? (
<Overlay position='top-[-0.5rem] left-[5.5rem] cc-icons'> <Overlay position='top-[-0.5rem] left-[5.5rem]' className='cc-icons'>
<div className='flex items-start'>
<MiniButton <MiniButton
title='Изменить редакторов' title='Изменить редакторов'
noHover noHover
@ -98,7 +97,6 @@ function EditorLibraryItem({ item, isModified, controller }: EditorLibraryItemPr
icon={<IconEdit size='1rem' className='mt-1 icon-primary' />} icon={<IconEdit size='1rem' className='mt-1 icon-primary' />}
disabled={isModified || controller.isProcessing} disabled={isModified || controller.isProcessing}
/> />
</div>
</Overlay> </Overlay>
) : null} ) : null}
<LabeledValue <LabeledValue