mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-08-14 04:40:36 +03:00
M: Minor UI improvements
This commit is contained in:
parent
72f7be3247
commit
de108074b1
|
@ -12,6 +12,7 @@ import {
|
||||||
IconGraphInputs,
|
IconGraphInputs,
|
||||||
IconGraphMaximize,
|
IconGraphMaximize,
|
||||||
IconGraphOutputs,
|
IconGraphOutputs,
|
||||||
|
IconGraphSelection,
|
||||||
IconNewItem,
|
IconNewItem,
|
||||||
IconOSS,
|
IconOSS,
|
||||||
IconPredecessor,
|
IconPredecessor,
|
||||||
|
@ -101,6 +102,9 @@ export function HelpRSGraphTerm() {
|
||||||
<div className='dense w-84'>
|
<div className='dense w-84'>
|
||||||
<h2>Выделение</h2>
|
<h2>Выделение</h2>
|
||||||
<ul>
|
<ul>
|
||||||
|
<li>
|
||||||
|
<IconGraphSelection className='inline-icon' /> выделить связанные...
|
||||||
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<IconGraphCollapse className='inline-icon' /> все влияющие
|
<IconGraphCollapse className='inline-icon' /> все влияющие
|
||||||
</li>
|
</li>
|
||||||
|
|
|
@ -73,7 +73,7 @@ export function DlgCreateSynthesis() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ModalForm
|
<ModalForm
|
||||||
header='Создание операции'
|
header='Создание операции синтеза'
|
||||||
submitText='Создать'
|
submitText='Создать'
|
||||||
canSubmit={isValid}
|
canSubmit={isValid}
|
||||||
onSubmit={event => void methods.handleSubmit(onSubmit)(event)}
|
onSubmit={event => void methods.handleSubmit(onSubmit)(event)}
|
||||||
|
|
|
@ -101,18 +101,15 @@ export function DlgEditOperation() {
|
||||||
<TabOperation />
|
<TabOperation />
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
|
|
||||||
{target.operation_type === OperationType.SYNTHESIS ? (
|
<TabPanel>{target.operation_type === OperationType.SYNTHESIS ? <TabArguments /> : null}</TabPanel>
|
||||||
<TabPanel>
|
|
||||||
<TabArguments />
|
<TabPanel>
|
||||||
</TabPanel>
|
{target.operation_type === OperationType.SYNTHESIS ? (
|
||||||
) : null}
|
|
||||||
{target.operation_type === OperationType.SYNTHESIS ? (
|
|
||||||
<TabPanel>
|
|
||||||
<Suspense fallback={<Loader />}>
|
<Suspense fallback={<Loader />}>
|
||||||
<TabSubstitutions />
|
<TabSubstitutions />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</TabPanel>
|
) : null}
|
||||||
) : null}
|
</TabPanel>
|
||||||
</FormProvider>
|
</FormProvider>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</ModalForm>
|
</ModalForm>
|
||||||
|
|
|
@ -77,7 +77,7 @@ export function OssFlow() {
|
||||||
void updateLayout({ itemID: schema.id, data: getLayout() });
|
void updateLayout({ itemID: schema.id, data: getLayout() });
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleCreateOperation() {
|
function handleCreateSynthesis() {
|
||||||
const targetPosition = screenToFlowPosition({ x: window.innerWidth / 2, y: window.innerHeight / 2 });
|
const targetPosition = screenToFlowPosition({ x: window.innerWidth / 2, y: window.innerHeight / 2 });
|
||||||
showCreateOperation({
|
showCreateOperation({
|
||||||
manager: new LayoutManager(schema, getLayout()),
|
manager: new LayoutManager(schema, getLayout()),
|
||||||
|
@ -184,13 +184,23 @@ export function OssFlow() {
|
||||||
withPreventDefault(handleSavePositions)(event);
|
withPreventDefault(handleSavePositions)(event);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (event.altKey && event.code === 'Key1') {
|
if (event.altKey) {
|
||||||
withPreventDefault(handleCreateBlock)(event);
|
if (event.code === 'Digit1') {
|
||||||
return;
|
withPreventDefault(handleCreateBlock)(event);
|
||||||
}
|
return;
|
||||||
if (event.altKey && event.code === 'Key2') {
|
}
|
||||||
withPreventDefault(handleCreateOperation)(event);
|
if (event.code === 'Digit2') {
|
||||||
return;
|
withPreventDefault(handleCreateSynthesis)(event);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (event.code === 'Digit3') {
|
||||||
|
withPreventDefault(handleImportSchema)(event);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (event.code === 'Digit4') {
|
||||||
|
withPreventDefault(handleCreateSynthesis)(event);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (event.key === 'Delete') {
|
if (event.key === 'Delete') {
|
||||||
withPreventDefault(handleDeleteSelected)(event);
|
withPreventDefault(handleDeleteSelected)(event);
|
||||||
|
@ -220,7 +230,7 @@ export function OssFlow() {
|
||||||
onCreateBlock={handleCreateBlock}
|
onCreateBlock={handleCreateBlock}
|
||||||
onCreateSchema={handleCreateSchema}
|
onCreateSchema={handleCreateSchema}
|
||||||
onImportSchema={handleImportSchema}
|
onImportSchema={handleImportSchema}
|
||||||
onCreateSynthesis={handleCreateOperation}
|
onCreateSynthesis={handleCreateSynthesis}
|
||||||
onDelete={handleDeleteSelected}
|
onDelete={handleDeleteSelected}
|
||||||
onResetPositions={resetGraph}
|
onResetPositions={resetGraph}
|
||||||
openContextMenu={openContextMenu}
|
openContextMenu={openContextMenu}
|
||||||
|
|
|
@ -113,9 +113,8 @@ export function ToolbarOssGraph({
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
'cc-tab-tools flex flex-col items-center',
|
'grid justify-items-center', //
|
||||||
'rounded-b-2xl',
|
'rounded-b-2xl hover:bg-background backdrop-blur-xs',
|
||||||
'hover:bg-background backdrop-blur-xs',
|
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
|
|
|
@ -44,7 +44,7 @@ export function SchemasGuide({ schema }: SchemasGuideProps) {
|
||||||
<Tooltip
|
<Tooltip
|
||||||
anchorSelect={`#${globalIDs.graph_schemas}`}
|
anchorSelect={`#${globalIDs.graph_schemas}`}
|
||||||
place='right'
|
place='right'
|
||||||
className='grid max-w-100 break-words text-base'
|
className='z-topmost grid max-w-100 break-words text-base'
|
||||||
>
|
>
|
||||||
<div className='inline-flex items-center gap-2'>
|
<div className='inline-flex items-center gap-2'>
|
||||||
<span className='w-3 h-3 border rounded-full' style={{ backgroundColor: colorBgSchemas(0) }} />
|
<span className='w-3 h-3 border rounded-full' style={{ backgroundColor: colorBgSchemas(0) }} />
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { MiniButton } from '@/components/control';
|
import { MiniButton } from '@/components/control';
|
||||||
|
import { Dropdown, DropdownButton, useDropdown } from '@/components/dropdown';
|
||||||
import {
|
import {
|
||||||
IconGraphCollapse,
|
IconGraphCollapse,
|
||||||
IconGraphCore,
|
IconGraphCore,
|
||||||
|
@ -7,6 +8,7 @@ import {
|
||||||
IconGraphInverse,
|
IconGraphInverse,
|
||||||
IconGraphMaximize,
|
IconGraphMaximize,
|
||||||
IconGraphOutputs,
|
IconGraphOutputs,
|
||||||
|
IconGraphSelection,
|
||||||
IconPredecessor,
|
IconPredecessor,
|
||||||
IconReset
|
IconReset
|
||||||
} from '@/components/icons';
|
} from '@/components/icons';
|
||||||
|
@ -31,6 +33,7 @@ export function ToolbarGraphSelection({
|
||||||
onChange,
|
onChange,
|
||||||
...restProps
|
...restProps
|
||||||
}: ToolbarGraphSelectionProps) {
|
}: ToolbarGraphSelectionProps) {
|
||||||
|
const menu = useDropdown();
|
||||||
const emptySelection = selected.length === 0;
|
const emptySelection = selected.length === 0;
|
||||||
|
|
||||||
function handleSelectCore() {
|
function handleSelectCore() {
|
||||||
|
@ -43,61 +46,77 @@ export function ToolbarGraphSelection({
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cn('cc-icons', className)} {...restProps}>
|
<div className={cn('cc-icons items-center', className)} {...restProps}>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
title='Сбросить выделение'
|
title='Сбросить выделение'
|
||||||
icon={<IconReset size='1.25rem' className='icon-primary' />}
|
icon={<IconReset size='1.25rem' className='icon-primary' />}
|
||||||
onClick={() => onChange([])}
|
onClick={() => onChange([])}
|
||||||
disabled={emptySelection}
|
disabled={emptySelection}
|
||||||
/>
|
/>
|
||||||
|
<div ref={menu.ref} onBlur={menu.handleBlur} className='flex items-center relative'>
|
||||||
|
<MiniButton
|
||||||
|
title='Выделить...'
|
||||||
|
hideTitle={menu.isOpen}
|
||||||
|
icon={<IconGraphSelection size='1.25rem' className='icon-primary' />}
|
||||||
|
onClick={menu.toggle}
|
||||||
|
disabled={emptySelection}
|
||||||
|
/>
|
||||||
|
<Dropdown isOpen={menu.isOpen} className='-translate-x-1/2'>
|
||||||
|
<DropdownButton
|
||||||
|
text='Влияющие'
|
||||||
|
title='Выделить все влияющие'
|
||||||
|
icon={<IconGraphCollapse size='1.25rem' className='icon-primary' />}
|
||||||
|
onClick={() => onChange([...selected, ...graph.expandAllInputs(selected)])}
|
||||||
|
disabled={emptySelection}
|
||||||
|
/>
|
||||||
|
<DropdownButton
|
||||||
|
text='Зависимые'
|
||||||
|
title='Выделить все зависимые'
|
||||||
|
icon={<IconGraphExpand size='1.25rem' className='icon-primary' />}
|
||||||
|
onClick={() => onChange([...selected, ...graph.expandAllOutputs(selected)])}
|
||||||
|
disabled={emptySelection}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<DropdownButton
|
||||||
|
text='Поставщики'
|
||||||
|
title='Выделить поставщиков'
|
||||||
|
icon={<IconGraphInputs size='1.25rem' className='icon-primary' />}
|
||||||
|
onClick={() => onChange([...selected, ...graph.expandInputs(selected)])}
|
||||||
|
disabled={emptySelection}
|
||||||
|
/>
|
||||||
|
<DropdownButton
|
||||||
|
text='Потребители'
|
||||||
|
title='Выделить потребителей'
|
||||||
|
icon={<IconGraphOutputs size='1.25rem' className='icon-primary' />}
|
||||||
|
onClick={() => onChange([...selected, ...graph.expandOutputs(selected)])}
|
||||||
|
disabled={emptySelection}
|
||||||
|
/>
|
||||||
|
<DropdownButton
|
||||||
|
text='Максимизация'
|
||||||
|
titleHtml='<b>Максимизация</b> <br/>дополнение выделения конституентами, <br/>зависимыми только от выделенных'
|
||||||
|
aria-label='Максимизация - дополнение выделения конституентами, зависимыми только от выделенных'
|
||||||
|
icon={<IconGraphMaximize size='1.25rem' className='icon-primary' />}
|
||||||
|
onClick={() => onChange(graph.maximizePart(selected))}
|
||||||
|
disabled={emptySelection}
|
||||||
|
/>
|
||||||
|
</Dropdown>
|
||||||
|
</div>
|
||||||
|
|
||||||
<MiniButton
|
<MiniButton
|
||||||
title='Выделить все влияющие'
|
title='Выделить ядро'
|
||||||
icon={<IconGraphCollapse size='1.25rem' className='icon-primary' />}
|
icon={<IconGraphCore size='1.25rem' className='icon-primary' />}
|
||||||
onClick={() => onChange([...selected, ...graph.expandAllInputs(selected)])}
|
onClick={handleSelectCore}
|
||||||
disabled={emptySelection}
|
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
title='Выделить все зависимые'
|
title='Выделить собственные'
|
||||||
icon={<IconGraphExpand size='1.25rem' className='icon-primary' />}
|
icon={<IconPredecessor size='1.25rem' className='icon-primary' />}
|
||||||
onClick={() => onChange([...selected, ...graph.expandAllOutputs(selected)])}
|
onClick={handleSelectOwned}
|
||||||
disabled={emptySelection}
|
|
||||||
/>
|
|
||||||
<MiniButton
|
|
||||||
titleHtml='<b>Максимизация</b> <br/>дополнение выделения конституентами, <br/>зависимыми только от выделенных'
|
|
||||||
aria-label='Максимизация - дополнение выделения конституентами, зависимыми только от выделенных'
|
|
||||||
icon={<IconGraphMaximize size='1.25rem' className='icon-primary' />}
|
|
||||||
onClick={() => onChange(graph.maximizePart(selected))}
|
|
||||||
disabled={emptySelection}
|
|
||||||
/>
|
|
||||||
<MiniButton
|
|
||||||
title='Выделить поставщиков'
|
|
||||||
icon={<IconGraphInputs size='1.25rem' className='icon-primary' />}
|
|
||||||
onClick={() => onChange([...selected, ...graph.expandInputs(selected)])}
|
|
||||||
disabled={emptySelection}
|
|
||||||
/>
|
|
||||||
<MiniButton
|
|
||||||
title='Выделить потребителей'
|
|
||||||
icon={<IconGraphOutputs size='1.25rem' className='icon-primary' />}
|
|
||||||
onClick={() => onChange([...selected, ...graph.expandOutputs(selected)])}
|
|
||||||
disabled={emptySelection}
|
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
title='Инвертировать'
|
title='Инвертировать'
|
||||||
icon={<IconGraphInverse size='1.25rem' className='icon-primary' />}
|
icon={<IconGraphInverse size='1.25rem' className='icon-primary' />}
|
||||||
onClick={() => onChange([...graph.nodes.keys()].filter(item => !selected.includes(item)))}
|
onClick={() => onChange([...graph.nodes.keys()].filter(item => !selected.includes(item)))}
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
|
||||||
title='Выделить ядро'
|
|
||||||
icon={<IconGraphCore size='1.25rem' className='icon-primary' />}
|
|
||||||
onClick={handleSelectCore}
|
|
||||||
/>
|
|
||||||
{isOwned ? (
|
|
||||||
<MiniButton
|
|
||||||
title='Выделить собственные'
|
|
||||||
icon={<IconPredecessor size='1.25rem' className='icon-primary' />}
|
|
||||||
onClick={handleSelectOwned}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
import { useEffect, useRef } from 'react';
|
import { useEffect, useRef } from 'react';
|
||||||
import { type Edge, MarkerType, type Node, useEdgesState, useNodesState, useOnSelectionChange } from 'reactflow';
|
import { type Edge, MarkerType, type Node, useEdgesState, useNodesState, useOnSelectionChange } from 'reactflow';
|
||||||
|
|
||||||
import { DiagramFlow, useReactFlow, useStoreApi } from '@/components/flow/diagram-flow';
|
import { DiagramFlow, useReactFlow } from '@/components/flow/diagram-flow';
|
||||||
import { useMainHeight } from '@/stores/app-layout';
|
import { useMainHeight } from '@/stores/app-layout';
|
||||||
import { PARAMETER } from '@/utils/constants';
|
import { PARAMETER } from '@/utils/constants';
|
||||||
import { withPreventDefault } from '@/utils/utils';
|
import { withPreventDefault } from '@/utils/utils';
|
||||||
|
@ -12,10 +12,7 @@ import { useMutatingRSForm } from '../../../backend/use-mutating-rsform';
|
||||||
import { TGEdgeTypes } from '../../../components/term-graph/graph/tg-edge-types';
|
import { TGEdgeTypes } from '../../../components/term-graph/graph/tg-edge-types';
|
||||||
import { TGNodeTypes } from '../../../components/term-graph/graph/tg-node-types';
|
import { TGNodeTypes } from '../../../components/term-graph/graph/tg-node-types';
|
||||||
import { SelectColoring } from '../../../components/term-graph/select-coloring';
|
import { SelectColoring } from '../../../components/term-graph/select-coloring';
|
||||||
import { ToolbarFocusedCst } from '../../../components/term-graph/toolbar-focused-cst';
|
|
||||||
import { ToolbarGraphSelection } from '../../../components/toolbar-graph-selection';
|
|
||||||
import { applyLayout, type TGNodeData } from '../../../models/graph-api';
|
import { applyLayout, type TGNodeData } from '../../../models/graph-api';
|
||||||
import { isBasicConcept } from '../../../models/rsform-api';
|
|
||||||
import { useTermGraphStore } from '../../../stores/term-graph';
|
import { useTermGraphStore } from '../../../stores/term-graph';
|
||||||
import { useRSEdit } from '../rsedit-context';
|
import { useRSEdit } from '../rsedit-context';
|
||||||
|
|
||||||
|
@ -36,20 +33,9 @@ export const flowOptions = {
|
||||||
export function TGFlow() {
|
export function TGFlow() {
|
||||||
const mainHeight = useMainHeight();
|
const mainHeight = useMainHeight();
|
||||||
const { fitView, viewportInitialized } = useReactFlow();
|
const { fitView, viewportInitialized } = useReactFlow();
|
||||||
const store = useStoreApi();
|
|
||||||
const { addSelectedNodes } = store.getState();
|
|
||||||
const isProcessing = useMutatingRSForm();
|
const isProcessing = useMutatingRSForm();
|
||||||
const {
|
const { isContentEditable, schema, selected, setSelected, promptDeleteCst, focusCst, setFocus, navigateCst } =
|
||||||
isContentEditable,
|
useRSEdit();
|
||||||
schema,
|
|
||||||
selected,
|
|
||||||
setSelected,
|
|
||||||
promptDeleteCst,
|
|
||||||
focusCst,
|
|
||||||
setFocus,
|
|
||||||
deselectAll,
|
|
||||||
navigateCst
|
|
||||||
} = useRSEdit();
|
|
||||||
|
|
||||||
const [nodes, setNodes, onNodesChange] = useNodesState<Node>([]);
|
const [nodes, setNodes, onNodesChange] = useNodesState<Node>([]);
|
||||||
const [edges, setEdges] = useEdgesState<Edge>([]);
|
const [edges, setEdges] = useEdgesState<Edge>([]);
|
||||||
|
@ -59,11 +45,8 @@ export function TGFlow() {
|
||||||
|
|
||||||
function onSelectionChange({ nodes }: { nodes: Node[] }) {
|
function onSelectionChange({ nodes }: { nodes: Node[] }) {
|
||||||
const ids = nodes.map(node => Number(node.id));
|
const ids = nodes.map(node => Number(node.id));
|
||||||
if (ids.length === 0) {
|
|
||||||
deselectAll();
|
setSelected(prev => [...prev.filter(nodeID => !filteredGraph.hasNode(nodeID)), ...ids]);
|
||||||
} else {
|
|
||||||
setSelected(prev => [...prev.filter(nodeID => !filteredGraph.hasNode(nodeID)), ...ids]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
useOnSelectionChange({
|
useOnSelectionChange({
|
||||||
onChange: onSelectionChange
|
onChange: onSelectionChange
|
||||||
|
@ -130,11 +113,6 @@ export function TGFlow() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSetSelected(newSelection: number[]) {
|
|
||||||
setSelected(newSelection);
|
|
||||||
addSelectedNodes(newSelection.map(id => String(id)));
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleKeyDown(event: React.KeyboardEvent<HTMLDivElement>) {
|
function handleKeyDown(event: React.KeyboardEvent<HTMLDivElement>) {
|
||||||
if (isProcessing) {
|
if (isProcessing) {
|
||||||
return;
|
return;
|
||||||
|
@ -166,26 +144,7 @@ export function TGFlow() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='relative' tabIndex={-1} onKeyDown={handleKeyDown}>
|
<div className='relative' tabIndex={-1} onKeyDown={handleKeyDown}>
|
||||||
<div className='cc-tab-tools flex flex-col items-center rounded-b-2xl backdrop-blur-xs'>
|
<ToolbarTermGraph className='cc-tab-tools' />
|
||||||
<ToolbarTermGraph />
|
|
||||||
{focusCst ? (
|
|
||||||
<ToolbarFocusedCst
|
|
||||||
focus={focusCst} //
|
|
||||||
resetFocus={() => setFocus(null)}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<ToolbarGraphSelection
|
|
||||||
graph={schema.graph}
|
|
||||||
isCore={cstID => {
|
|
||||||
const cst = schema.cstByID.get(cstID);
|
|
||||||
return !!cst && isBasicConcept(cst.cst_type);
|
|
||||||
}}
|
|
||||||
isOwned={schema.inheritance.length > 0 ? cstID => !schema.cstByID.get(cstID)?.is_inherited : undefined}
|
|
||||||
value={selected}
|
|
||||||
onChange={handleSetSelected}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='absolute z-pop top-24 sm:top-16 left-2 sm:left-3 w-54 flex flex-col pointer-events-none'>
|
<div className='absolute z-pop top-24 sm:top-16 left-2 sm:left-3 w-54 flex flex-col pointer-events-none'>
|
||||||
<span className='px-2 pb-1 select-none whitespace-nowrap backdrop-blur-xs rounded-xl w-fit'>
|
<span className='px-2 pb-1 select-none whitespace-nowrap backdrop-blur-xs rounded-xl w-fit'>
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
import { useReactFlow } from 'reactflow';
|
import { useReactFlow, useStoreApi } from 'reactflow';
|
||||||
|
|
||||||
import { HelpTopic } from '@/features/help';
|
import { HelpTopic } from '@/features/help';
|
||||||
import { BadgeHelp } from '@/features/help/components/badge-help';
|
import { BadgeHelp } from '@/features/help/components/badge-help';
|
||||||
import { type ILibraryItemReference } from '@/features/library';
|
import { type ILibraryItemReference } from '@/features/library';
|
||||||
import { MiniSelectorOSS } from '@/features/library/components/mini-selector-oss';
|
import { MiniSelectorOSS } from '@/features/library/components/mini-selector-oss';
|
||||||
|
import { ToolbarFocusedCst } from '@/features/rsform/components/term-graph/toolbar-focused-cst';
|
||||||
|
import { ToolbarGraphSelection } from '@/features/rsform/components/toolbar-graph-selection';
|
||||||
|
import { isBasicConcept } from '@/features/rsform/models/rsform-api';
|
||||||
|
|
||||||
import { MiniButton } from '@/components/control';
|
import { MiniButton } from '@/components/control';
|
||||||
import {
|
import {
|
||||||
|
@ -18,6 +21,7 @@ import {
|
||||||
IconTextOff,
|
IconTextOff,
|
||||||
IconTypeGraph
|
IconTypeGraph
|
||||||
} from '@/components/icons';
|
} from '@/components/icons';
|
||||||
|
import { cn } from '@/components/utils';
|
||||||
import { useDialogsStore } from '@/stores/dialogs';
|
import { useDialogsStore } from '@/stores/dialogs';
|
||||||
import { PARAMETER } from '@/utils/constants';
|
import { PARAMETER } from '@/utils/constants';
|
||||||
|
|
||||||
|
@ -28,17 +32,23 @@ import { useRSEdit } from '../rsedit-context';
|
||||||
|
|
||||||
import { flowOptions } from './tg-flow';
|
import { flowOptions } from './tg-flow';
|
||||||
|
|
||||||
export function ToolbarTermGraph() {
|
interface ToolbarTermGraphProps {
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ToolbarTermGraph({ className }: ToolbarTermGraphProps) {
|
||||||
const isProcessing = useMutatingRSForm();
|
const isProcessing = useMutatingRSForm();
|
||||||
const {
|
const {
|
||||||
schema, //
|
schema,
|
||||||
selected,
|
selected,
|
||||||
|
setSelected,
|
||||||
setFocus,
|
setFocus,
|
||||||
navigateOss,
|
navigateOss,
|
||||||
isContentEditable,
|
isContentEditable,
|
||||||
canDeleteSelected,
|
canDeleteSelected,
|
||||||
createCst,
|
createCst,
|
||||||
promptDeleteCst
|
promptDeleteCst,
|
||||||
|
focusCst
|
||||||
} = useRSEdit();
|
} = useRSEdit();
|
||||||
const showTypeGraph = useDialogsStore(state => state.showShowTypeGraph);
|
const showTypeGraph = useDialogsStore(state => state.showShowTypeGraph);
|
||||||
const showParams = useDialogsStore(state => state.showGraphParams);
|
const showParams = useDialogsStore(state => state.showGraphParams);
|
||||||
|
@ -47,6 +57,8 @@ export function ToolbarTermGraph() {
|
||||||
const toggleClustering = useTermGraphStore(state => state.toggleClustering);
|
const toggleClustering = useTermGraphStore(state => state.toggleClustering);
|
||||||
|
|
||||||
const { fitView } = useReactFlow();
|
const { fitView } = useReactFlow();
|
||||||
|
const store = useStoreApi();
|
||||||
|
const { addSelectedNodes } = store.getState();
|
||||||
|
|
||||||
function handleShowTypeGraph() {
|
function handleShowTypeGraph() {
|
||||||
const typeInfo = schema.items.map(item => ({
|
const typeInfo = schema.items.map(item => ({
|
||||||
|
@ -86,69 +98,102 @@ export function ToolbarTermGraph() {
|
||||||
navigateOss(newValue.id, event.ctrlKey || event.metaKey);
|
navigateOss(newValue.id, event.ctrlKey || event.metaKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleSetSelected(newSelection: number[]) {
|
||||||
|
setSelected(newSelection);
|
||||||
|
addSelectedNodes(newSelection.map(id => String(id)));
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='cc-icons'>
|
<div
|
||||||
{schema.oss.length > 0 ? <MiniSelectorOSS items={schema.oss} onSelect={handleSelectOss} /> : null}
|
className={cn(
|
||||||
<MiniButton
|
'grid justify-items-center', //
|
||||||
title='Настройки фильтрации узлов и связей'
|
'rounded-b-2xl hover:bg-background backdrop-blur-xs',
|
||||||
icon={<IconFilter size='1.25rem' className='icon-primary' />}
|
className
|
||||||
onClick={showParams}
|
)}
|
||||||
/>
|
>
|
||||||
<MiniButton
|
<div className='cc-icons'>
|
||||||
title='Задать фокус конституенту'
|
{schema.oss.length > 0 ? <MiniSelectorOSS items={schema.oss} onSelect={handleSelectOss} /> : null}
|
||||||
icon={<IconFocus size='1.25rem' className='icon-primary' />}
|
|
||||||
disabled={selected.length !== 1}
|
|
||||||
onClick={handleSetFocus}
|
|
||||||
/>
|
|
||||||
<MiniButton
|
|
||||||
title='Граф целиком'
|
|
||||||
icon={<IconFitImage size='1.25rem' className='icon-primary' />}
|
|
||||||
onClick={handleFitView}
|
|
||||||
/>
|
|
||||||
<MiniButton
|
|
||||||
title={!filter.noText ? 'Скрыть текст' : 'Отобразить текст'}
|
|
||||||
icon={
|
|
||||||
!filter.noText ? (
|
|
||||||
<IconText size='1.25rem' className='icon-green' />
|
|
||||||
) : (
|
|
||||||
<IconTextOff size='1.25rem' className='icon-primary' />
|
|
||||||
)
|
|
||||||
}
|
|
||||||
onClick={toggleText}
|
|
||||||
/>
|
|
||||||
<MiniButton
|
|
||||||
title={!filter.foldDerived ? 'Скрыть порожденные' : 'Отобразить порожденные'}
|
|
||||||
icon={
|
|
||||||
!filter.foldDerived ? (
|
|
||||||
<IconClustering size='1.25rem' className='icon-green' />
|
|
||||||
) : (
|
|
||||||
<IconClusteringOff size='1.25rem' className='icon-primary' />
|
|
||||||
)
|
|
||||||
}
|
|
||||||
onClick={toggleClustering}
|
|
||||||
/>
|
|
||||||
{isContentEditable ? (
|
|
||||||
<MiniButton
|
<MiniButton
|
||||||
title='Новая конституента'
|
title='Настройки фильтрации узлов и связей'
|
||||||
icon={<IconNewItem size='1.25rem' className='icon-green' />}
|
icon={<IconFilter size='1.25rem' className='icon-primary' />}
|
||||||
onClick={handleCreateCst}
|
onClick={showParams}
|
||||||
disabled={isProcessing}
|
|
||||||
/>
|
/>
|
||||||
) : null}
|
|
||||||
{isContentEditable ? (
|
|
||||||
<MiniButton
|
<MiniButton
|
||||||
title='Удалить выбранные'
|
title='Задать фокус конституенту'
|
||||||
icon={<IconDestroy size='1.25rem' className='icon-red' />}
|
icon={<IconFocus size='1.25rem' className='icon-primary' />}
|
||||||
onClick={handleDeleteCst}
|
disabled={selected.length !== 1}
|
||||||
disabled={!canDeleteSelected || isProcessing}
|
onClick={handleSetFocus}
|
||||||
/>
|
/>
|
||||||
) : null}
|
<MiniButton
|
||||||
<MiniButton
|
title='Граф целиком'
|
||||||
icon={<IconTypeGraph size='1.25rem' className='icon-primary' />}
|
icon={<IconFitImage size='1.25rem' className='icon-primary' />}
|
||||||
title='Граф ступеней'
|
onClick={handleFitView}
|
||||||
onClick={handleShowTypeGraph}
|
/>
|
||||||
/>
|
<MiniButton
|
||||||
<BadgeHelp topic={HelpTopic.UI_GRAPH_TERM} contentClass='sm:max-w-160' offset={4} />
|
title={!filter.noText ? 'Скрыть текст' : 'Отобразить текст'}
|
||||||
|
icon={
|
||||||
|
!filter.noText ? (
|
||||||
|
<IconText size='1.25rem' className='icon-green' />
|
||||||
|
) : (
|
||||||
|
<IconTextOff size='1.25rem' className='icon-primary' />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
onClick={toggleText}
|
||||||
|
/>
|
||||||
|
<MiniButton
|
||||||
|
title={!filter.foldDerived ? 'Скрыть порожденные' : 'Отобразить порожденные'}
|
||||||
|
icon={
|
||||||
|
!filter.foldDerived ? (
|
||||||
|
<IconClustering size='1.25rem' className='icon-green' />
|
||||||
|
) : (
|
||||||
|
<IconClusteringOff size='1.25rem' className='icon-primary' />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
onClick={toggleClustering}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<MiniButton
|
||||||
|
icon={<IconTypeGraph size='1.25rem' className='icon-primary' />}
|
||||||
|
title='Граф ступеней'
|
||||||
|
onClick={handleShowTypeGraph}
|
||||||
|
/>
|
||||||
|
<BadgeHelp topic={HelpTopic.UI_GRAPH_TERM} contentClass='sm:max-w-160' offset={4} />
|
||||||
|
</div>
|
||||||
|
<div className='cc-icons items-center'>
|
||||||
|
{focusCst ? (
|
||||||
|
<ToolbarFocusedCst
|
||||||
|
focus={focusCst} //
|
||||||
|
resetFocus={() => setFocus(null)}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<ToolbarGraphSelection
|
||||||
|
graph={schema.graph}
|
||||||
|
isCore={cstID => {
|
||||||
|
const cst = schema.cstByID.get(cstID);
|
||||||
|
return !!cst && isBasicConcept(cst.cst_type);
|
||||||
|
}}
|
||||||
|
isOwned={schema.inheritance.length > 0 ? cstID => !schema.cstByID.get(cstID)?.is_inherited : undefined}
|
||||||
|
value={selected}
|
||||||
|
onChange={handleSetSelected}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{isContentEditable ? (
|
||||||
|
<MiniButton
|
||||||
|
title='Новая конституента'
|
||||||
|
icon={<IconNewItem size='1.25rem' className='icon-green' />}
|
||||||
|
onClick={handleCreateCst}
|
||||||
|
disabled={isProcessing}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
{isContentEditable ? (
|
||||||
|
<MiniButton
|
||||||
|
title='Удалить выбранные'
|
||||||
|
icon={<IconDestroy size='1.25rem' className='icon-red' />}
|
||||||
|
onClick={handleDeleteCst}
|
||||||
|
disabled={!canDeleteSelected || isProcessing}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user