mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 04:50:36 +03:00
This commit is contained in:
parent
083f6bf299
commit
4f9b48cce5
|
@ -5,6 +5,7 @@ import {
|
|||
IconDatabase,
|
||||
IconHelp,
|
||||
IconHelpOff,
|
||||
IconImage,
|
||||
IconLightTheme,
|
||||
IconLogout,
|
||||
IconUser
|
||||
|
@ -43,6 +44,11 @@ function UserDropdown({ isOpen, hideDropdown }: UserDropdownProps) {
|
|||
logout(() => router.push(urls.admin, true));
|
||||
}
|
||||
|
||||
function gotoIcons(event: CProps.EventMouse) {
|
||||
hideDropdown();
|
||||
router.push(urls.icons, event.ctrlKey || event.metaKey);
|
||||
}
|
||||
|
||||
function handleToggleDarkMode() {
|
||||
hideDropdown();
|
||||
toggleDarkMode();
|
||||
|
@ -77,7 +83,18 @@ function UserDropdown({ isOpen, hideDropdown }: UserDropdownProps) {
|
|||
/>
|
||||
) : null}
|
||||
{user?.is_staff ? (
|
||||
<DropdownButton text='База данных' icon={<IconDatabase size='1rem' />} onClick={gotoAdmin} />
|
||||
<DropdownButton
|
||||
text='База данных' // prettier: split-line
|
||||
icon={<IconDatabase size='1rem' />}
|
||||
onClick={gotoAdmin}
|
||||
/>
|
||||
) : null}
|
||||
{user?.is_staff ? (
|
||||
<DropdownButton
|
||||
text='Иконки' // prettier: split-line
|
||||
icon={<IconImage size='1rem' />}
|
||||
onClick={gotoIcons}
|
||||
/>
|
||||
) : null}
|
||||
<DropdownButton
|
||||
text='Выйти...'
|
||||
|
|
|
@ -43,6 +43,7 @@ export const urls = {
|
|||
login: `/${routes.login}`,
|
||||
login_hint: (userName: string) => `/login?username=${userName}`,
|
||||
profile: `/${routes.profile}`,
|
||||
icons: `/icons`,
|
||||
signup: `/${routes.signup}`,
|
||||
library: `/${routes.library}`,
|
||||
library_filter: (strategy: string) => `/library?filter=${strategy}`,
|
||||
|
|
|
@ -38,7 +38,6 @@ export { LuFolderClosed as IconFolderClosed } from 'react-icons/lu';
|
|||
export { LuFolderDot as IconFolderEmpty } from 'react-icons/lu';
|
||||
export { LuLightbulb as IconHelp } from 'react-icons/lu';
|
||||
export { LuLightbulbOff as IconHelpOff } from 'react-icons/lu';
|
||||
export { TbGridDots as IconGrid } from 'react-icons/tb';
|
||||
export { RiPushpinFill as IconPin } from 'react-icons/ri';
|
||||
export { RiUnpinLine as IconUnpin } from 'react-icons/ri';
|
||||
export { BiCaretDown as IconSortDesc } from 'react-icons/bi';
|
||||
|
@ -118,6 +117,11 @@ export { LuRotate3D as IconRotate3D } from 'react-icons/lu';
|
|||
export { MdOutlineFitScreen as IconFitImage } from 'react-icons/md';
|
||||
export { LuSparkles as IconClustering } from 'react-icons/lu';
|
||||
export { LuSparkle as IconClusteringOff } from 'react-icons/lu';
|
||||
export { TbGridDots as IconGrid } from 'react-icons/tb';
|
||||
export { FaSlash as IconLineStraight } from 'react-icons/fa6';
|
||||
export { PiWaveSineLight as IconLineWave } from 'react-icons/pi';
|
||||
export { LuCircleDashed as IconAnimation } from 'react-icons/lu';
|
||||
export { LuCircle as IconAnimationOff } from 'react-icons/lu';
|
||||
|
||||
// ===== Custom elements ======
|
||||
interface IconSVGProps {
|
||||
|
|
103
rsconcept/frontend/src/components/select/PickMultiOperation.tsx
Normal file
103
rsconcept/frontend/src/components/select/PickMultiOperation.tsx
Normal file
|
@ -0,0 +1,103 @@
|
|||
'use client';
|
||||
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
|
||||
import { IconRemove } from '@/components/Icons';
|
||||
import SelectOperation from '@/components/select/SelectOperation';
|
||||
import DataTable, { createColumnHelper } from '@/components/ui/DataTable';
|
||||
import MiniButton from '@/components/ui/MiniButton';
|
||||
import NoData from '@/components/ui/NoData';
|
||||
import { IOperation, OperationID } from '@/models/oss';
|
||||
|
||||
interface PickMultiOperationProps {
|
||||
rows?: number;
|
||||
|
||||
items: IOperation[];
|
||||
selected: OperationID[];
|
||||
setSelected: React.Dispatch<React.SetStateAction<OperationID[]>>;
|
||||
}
|
||||
|
||||
const columnHelper = createColumnHelper<IOperation>();
|
||||
|
||||
function PickMultiOperation({ rows, items, selected, setSelected }: PickMultiOperationProps) {
|
||||
const selectedItems = useMemo(() => items.filter(item => selected.includes(item.id)), [items, selected]);
|
||||
const nonSelectedItems = useMemo(() => items.filter(item => !selected.includes(item.id)), [items, selected]);
|
||||
const [lastSelected, setLastSelected] = useState<IOperation | undefined>(undefined);
|
||||
|
||||
const handleDelete = useCallback(
|
||||
(operation: OperationID) => setSelected(prev => prev.filter(item => item !== operation)),
|
||||
[setSelected]
|
||||
);
|
||||
|
||||
const handleSelect = useCallback(
|
||||
(operation?: IOperation) => {
|
||||
if (operation) {
|
||||
setLastSelected(operation);
|
||||
setSelected(prev => [...prev, operation.id]);
|
||||
setTimeout(() => setLastSelected(undefined), 1000);
|
||||
}
|
||||
},
|
||||
[setSelected]
|
||||
);
|
||||
|
||||
const columns = useMemo(
|
||||
() => [
|
||||
columnHelper.accessor('alias', {
|
||||
id: 'alias',
|
||||
header: 'Шифр',
|
||||
size: 150,
|
||||
minSize: 80,
|
||||
maxSize: 150
|
||||
}),
|
||||
columnHelper.accessor('title', {
|
||||
id: 'title',
|
||||
header: 'Название',
|
||||
size: 1200,
|
||||
minSize: 200,
|
||||
maxSize: 1200,
|
||||
cell: props => <div className='text-ellipsis'>{props.getValue()}</div>
|
||||
}),
|
||||
columnHelper.display({
|
||||
id: 'actions',
|
||||
cell: props => (
|
||||
<MiniButton
|
||||
noHover
|
||||
title='Удалить'
|
||||
icon={<IconRemove size='1rem' className='icon-red' />}
|
||||
onClick={() => handleDelete(props.row.original.id)}
|
||||
/>
|
||||
)
|
||||
})
|
||||
],
|
||||
[handleDelete]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className='flex flex-col gap-1 border-t border-x rounded-t-md clr-input'>
|
||||
<SelectOperation
|
||||
noBorder
|
||||
items={nonSelectedItems} // prettier: split-line
|
||||
value={lastSelected}
|
||||
onSelectValue={handleSelect}
|
||||
className='w-full'
|
||||
/>
|
||||
<DataTable
|
||||
dense
|
||||
noFooter
|
||||
rows={rows}
|
||||
contentHeight='1.3rem'
|
||||
className='cc-scroll-y text-sm select-none border-y'
|
||||
data={selectedItems}
|
||||
columns={columns}
|
||||
headPosition='0rem'
|
||||
noDataComponent={
|
||||
<NoData>
|
||||
<p>Список пуст</p>
|
||||
</NoData>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default PickMultiOperation;
|
|
@ -15,7 +15,9 @@ interface SelectConstituentaProps extends CProps.Styling {
|
|||
items?: IConstituenta[];
|
||||
value?: IConstituenta;
|
||||
onSelectValue: (newValue?: IConstituenta) => void;
|
||||
|
||||
placeholder?: string;
|
||||
noBorder?: boolean;
|
||||
}
|
||||
|
||||
function SelectConstituenta({
|
||||
|
|
|
@ -13,7 +13,9 @@ interface SelectOperationProps extends CProps.Styling {
|
|||
items?: IOperation[];
|
||||
value?: IOperation;
|
||||
onSelectValue: (newValue?: IOperation) => void;
|
||||
|
||||
placeholder?: string;
|
||||
noBorder?: boolean;
|
||||
}
|
||||
|
||||
function SelectOperation({
|
||||
|
|
|
@ -13,8 +13,10 @@ import SelectSingle from '../ui/SelectSingle';
|
|||
interface SelectUserProps extends CProps.Styling {
|
||||
items?: IUserInfo[];
|
||||
value?: UserID;
|
||||
placeholder?: string;
|
||||
onSelectValue: (newValue: UserID) => void;
|
||||
|
||||
placeholder?: string;
|
||||
noBorder?: boolean;
|
||||
}
|
||||
|
||||
function SelectUser({
|
||||
|
|
|
@ -14,6 +14,9 @@ interface SelectVersionProps extends CProps.Styling {
|
|||
items?: IVersionInfo[];
|
||||
value?: VersionID;
|
||||
onSelectValue: (newValue?: VersionID) => void;
|
||||
|
||||
placeholder?: string;
|
||||
noBorder?: boolean;
|
||||
}
|
||||
|
||||
function SelectVersion({ id, className, items, value, onSelectValue, ...restProps }: SelectVersionProps) {
|
||||
|
|
|
@ -1,16 +1,13 @@
|
|||
'use client';
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import SelectOperation from '@/components/select/SelectOperation';
|
||||
import FlexColumn from '@/components/ui/FlexColumn';
|
||||
import Label from '@/components/ui/Label';
|
||||
import TextArea from '@/components/ui/TextArea';
|
||||
import TextInput from '@/components/ui/TextInput';
|
||||
import AnimateFade from '@/components/wrap/AnimateFade';
|
||||
import { IOperation, IOperationSchema, OperationID } from '@/models/oss';
|
||||
import { IOperationSchema, OperationID } from '@/models/oss';
|
||||
import { limits, patterns } from '@/utils/constants';
|
||||
|
||||
import PickMultiOperation from '../../components/select/PickMultiOperation';
|
||||
|
||||
interface TabSynthesisOperationProps {
|
||||
oss: IOperationSchema;
|
||||
alias: string;
|
||||
|
@ -34,22 +31,6 @@ function TabSynthesisOperation({
|
|||
inputs,
|
||||
setInputs
|
||||
}: TabSynthesisOperationProps) {
|
||||
const [left, setLeft] = useState<IOperation | undefined>(undefined);
|
||||
const [right, setRight] = useState<IOperation | undefined>(undefined);
|
||||
|
||||
console.log(inputs);
|
||||
|
||||
useEffect(() => {
|
||||
const inputs: OperationID[] = [];
|
||||
if (left) {
|
||||
inputs.push(left.id);
|
||||
}
|
||||
if (right) {
|
||||
inputs.push(right.id);
|
||||
}
|
||||
setInputs(inputs);
|
||||
}, [setInputs, left, right]);
|
||||
|
||||
return (
|
||||
<AnimateFade className='cc-column'>
|
||||
<TextInput
|
||||
|
@ -79,16 +60,10 @@ function TabSynthesisOperation({
|
|||
/>
|
||||
</div>
|
||||
|
||||
<div className='flex justify-between'>
|
||||
<FlexColumn>
|
||||
<Label text='Аргумент 1' />
|
||||
<SelectOperation items={oss.items} value={left} onSelectValue={setLeft} />
|
||||
</FlexColumn>
|
||||
<FlexColumn>
|
||||
<Label text='Аргумент 2' className='text-right' />
|
||||
<SelectOperation items={oss.items} value={right} onSelectValue={setRight} />
|
||||
</FlexColumn>
|
||||
</div>
|
||||
<FlexColumn>
|
||||
<Label text={`Выбор аргументов: [ ${inputs.length} ]`} />
|
||||
<PickMultiOperation items={oss.items} selected={inputs} setSelected={setInputs} rows={6} />
|
||||
</FlexColumn>
|
||||
</AnimateFade>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -40,6 +40,7 @@ export interface OssNodeInternal {
|
|||
label: string;
|
||||
operation: IOperation;
|
||||
};
|
||||
dragging: boolean;
|
||||
xPos: number;
|
||||
yPos: number;
|
||||
}
|
||||
|
|
|
@ -4,18 +4,17 @@
|
|||
import * as icons from '@/components/Icons';
|
||||
|
||||
export function IconsPage() {
|
||||
const iconsList = Object.keys(icons).filter(key => key.startsWith('Icon'));
|
||||
return (
|
||||
<div className='flex flex-col items-center px-6 py-3'>
|
||||
<h1 className='mb-6'>Список иконок</h1>
|
||||
<h1 className='mb-6'>Всего иконок: {iconsList.length}</h1>
|
||||
<div className='grid grid-cols-4'>
|
||||
{Object.keys(icons)
|
||||
.filter(key => key.startsWith('Icon'))
|
||||
.map((key, index) => (
|
||||
<div key={`icons_list_${index}`} className='flex flex-col items-center px-3 pb-6'>
|
||||
<p>{icons[key]({ size: '2rem' })}</p>
|
||||
<p>{key}</p>
|
||||
</div>
|
||||
))}
|
||||
{iconsList.map((key, index) => (
|
||||
<div key={`icons_list_${index}`} className='flex flex-col items-center px-3 pb-6'>
|
||||
<p>{icons[key]({ size: '2rem' })}</p>
|
||||
<p>{key}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -2,9 +2,6 @@
|
|||
|
||||
import { ReactFlowProvider } from 'reactflow';
|
||||
|
||||
import useLocalStorage from '@/hooks/useLocalStorage';
|
||||
import { storage } from '@/utils/constants';
|
||||
|
||||
import OssFlow from './OssFlow';
|
||||
|
||||
interface EditorOssGraphProps {
|
||||
|
@ -13,11 +10,9 @@ interface EditorOssGraphProps {
|
|||
}
|
||||
|
||||
function EditorOssGraph({ isModified, setIsModified }: EditorOssGraphProps) {
|
||||
const [showGrid, setShowGrid] = useLocalStorage<boolean>(storage.ossShowGrid, false);
|
||||
|
||||
return (
|
||||
<ReactFlowProvider>
|
||||
<OssFlow isModified={isModified} setIsModified={setIsModified} showGrid={showGrid} setShowGrid={setShowGrid} />
|
||||
<OssFlow isModified={isModified} setIsModified={setIsModified} />
|
||||
</ReactFlowProvider>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ function InputNode(node: OssNodeInternal) {
|
|||
</Overlay>
|
||||
<div id={`${prefixes.operation_list}${node.id}`} className='flex-grow text-center text-sm'>
|
||||
{node.data.label}
|
||||
{controller.showTooltip ? (
|
||||
{controller.showTooltip && !node.dragging ? (
|
||||
<TooltipOperation anchor={`#${prefixes.operation_list}${node.id}`} node={node} />
|
||||
) : null}
|
||||
</div>
|
||||
|
|
|
@ -36,7 +36,9 @@ function OperationNode(node: OssNodeInternal) {
|
|||
|
||||
<div id={`${prefixes.operation_list}${node.id}`} className='flex-grow text-center text-sm'>
|
||||
{node.data.label}
|
||||
<TooltipOperation anchor={`#${prefixes.operation_list}${node.id}`} node={node} />
|
||||
{controller.showTooltip && !node.dragging ? (
|
||||
<TooltipOperation anchor={`#${prefixes.operation_list}${node.id}`} node={node} />
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
<Handle type='target' position={Position.Top} id='left' style={{ left: 40 }} />
|
||||
|
|
|
@ -10,7 +10,6 @@ import {
|
|||
Node,
|
||||
NodeChange,
|
||||
NodeTypes,
|
||||
ProOptions,
|
||||
ReactFlow,
|
||||
useEdgesState,
|
||||
useNodesState,
|
||||
|
@ -23,9 +22,10 @@ import Overlay from '@/components/ui/Overlay';
|
|||
import AnimateFade from '@/components/wrap/AnimateFade';
|
||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
||||
import { useOSS } from '@/context/OssContext';
|
||||
import useLocalStorage from '@/hooks/useLocalStorage';
|
||||
import { OssNode } from '@/models/miscellaneous';
|
||||
import { OperationID } from '@/models/oss';
|
||||
import { PARAMETER } from '@/utils/constants';
|
||||
import { PARAMETER, storage } from '@/utils/constants';
|
||||
import { errors } from '@/utils/labels';
|
||||
|
||||
import { useOssEdit } from '../OssEditContext';
|
||||
|
@ -37,16 +37,18 @@ import ToolbarOssGraph from './ToolbarOssGraph';
|
|||
interface OssFlowProps {
|
||||
isModified: boolean;
|
||||
setIsModified: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
showGrid: boolean;
|
||||
setShowGrid: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
}
|
||||
|
||||
function OssFlow({ isModified, setIsModified, showGrid, setShowGrid }: OssFlowProps) {
|
||||
function OssFlow({ isModified, setIsModified }: OssFlowProps) {
|
||||
const { calculateHeight, colors } = useConceptOptions();
|
||||
const model = useOSS();
|
||||
const controller = useOssEdit();
|
||||
const flow = useReactFlow();
|
||||
|
||||
const [showGrid, setShowGrid] = useLocalStorage<boolean>(storage.ossShowGrid, false);
|
||||
const [edgeAnimate, setEdgeAnimate] = useLocalStorage<boolean>(storage.ossEdgeAnimate, false);
|
||||
const [edgeStraight, setEdgeStraight] = useLocalStorage<boolean>(storage.ossEdgeStraight, false);
|
||||
|
||||
const [nodes, setNodes, onNodesChange] = useNodesState([]);
|
||||
const [edges, setEdges, onEdgesChange] = useEdgesState([]);
|
||||
const [toggleReset, setToggleReset] = useState(false);
|
||||
|
@ -81,6 +83,8 @@ function OssFlow({ isModified, setIsModified, showGrid, setShowGrid }: OssFlowPr
|
|||
id: String(index),
|
||||
source: String(argument.argument),
|
||||
target: String(argument.operation),
|
||||
type: edgeStraight ? 'straight' : 'bezier',
|
||||
animated: edgeAnimate,
|
||||
targetHandle:
|
||||
model.schema!.operationByID.get(argument.argument)!.position_x >
|
||||
model.schema!.operationByID.get(argument.operation)!.position_x
|
||||
|
@ -92,7 +96,7 @@ function OssFlow({ isModified, setIsModified, showGrid, setShowGrid }: OssFlowPr
|
|||
setTimeout(() => {
|
||||
setIsModified(false);
|
||||
}, PARAMETER.graphRefreshDelay);
|
||||
}, [model.schema, setNodes, setEdges, setIsModified, toggleReset]);
|
||||
}, [model.schema, setNodes, setEdges, setIsModified, toggleReset, edgeStraight, edgeAnimate]);
|
||||
|
||||
const getPositions = useCallback(
|
||||
() =>
|
||||
|
@ -224,7 +228,6 @@ function OssFlow({ isModified, setIsModified, showGrid, setShowGrid }: OssFlowPr
|
|||
}
|
||||
}
|
||||
|
||||
const proOptions: ProOptions = useMemo(() => ({ hideAttribution: true }), []);
|
||||
const canvasWidth = useMemo(() => 'calc(100vw - 1rem)', []);
|
||||
const canvasHeight = useMemo(() => calculateHeight('1.75rem + 4px'), [calculateHeight]);
|
||||
|
||||
|
@ -244,7 +247,7 @@ function OssFlow({ isModified, setIsModified, showGrid, setShowGrid }: OssFlowPr
|
|||
onNodesChange={handleNodesChange}
|
||||
onEdgesChange={onEdgesChange}
|
||||
fitView
|
||||
proOptions={proOptions}
|
||||
proOptions={{ hideAttribution: true }}
|
||||
nodeTypes={OssNodeTypes}
|
||||
maxZoom={2}
|
||||
minZoom={0.75}
|
||||
|
@ -257,17 +260,7 @@ function OssFlow({ isModified, setIsModified, showGrid, setShowGrid }: OssFlowPr
|
|||
{showGrid ? <Background gap={10} /> : null}
|
||||
</ReactFlow>
|
||||
),
|
||||
[
|
||||
nodes,
|
||||
edges,
|
||||
proOptions,
|
||||
handleNodesChange,
|
||||
handleContextMenu,
|
||||
handleClickCanvas,
|
||||
onEdgesChange,
|
||||
OssNodeTypes,
|
||||
showGrid
|
||||
]
|
||||
[nodes, edges, handleNodesChange, handleContextMenu, handleClickCanvas, onEdgesChange, OssNodeTypes, showGrid]
|
||||
);
|
||||
|
||||
return (
|
||||
|
@ -276,6 +269,8 @@ function OssFlow({ isModified, setIsModified, showGrid, setShowGrid }: OssFlowPr
|
|||
<ToolbarOssGraph
|
||||
isModified={isModified}
|
||||
showGrid={showGrid}
|
||||
edgeAnimate={edgeAnimate}
|
||||
edgeStraight={edgeStraight}
|
||||
onFitView={handleFitView}
|
||||
onCreate={handleCreateOperation}
|
||||
onDelete={handleDeleteSelected}
|
||||
|
@ -283,6 +278,8 @@ function OssFlow({ isModified, setIsModified, showGrid, setShowGrid }: OssFlowPr
|
|||
onSavePositions={handleSavePositions}
|
||||
onSaveImage={handleSaveImage}
|
||||
toggleShowGrid={() => setShowGrid(prev => !prev)}
|
||||
toggleEdgeAnimate={() => setEdgeAnimate(prev => !prev)}
|
||||
toggleEdgeStraight={() => setEdgeStraight(prev => !prev)}
|
||||
/>
|
||||
</Overlay>
|
||||
{menuProps ? (
|
||||
|
|
|
@ -1,6 +1,18 @@
|
|||
import clsx from 'clsx';
|
||||
|
||||
import { IconDestroy, IconFitImage, IconGrid, IconImage, IconNewItem, IconReset, IconSave } from '@/components/Icons';
|
||||
import {
|
||||
IconAnimation,
|
||||
IconAnimationOff,
|
||||
IconDestroy,
|
||||
IconFitImage,
|
||||
IconGrid,
|
||||
IconImage,
|
||||
IconLineStraight,
|
||||
IconLineWave,
|
||||
IconNewItem,
|
||||
IconReset,
|
||||
IconSave
|
||||
} from '@/components/Icons';
|
||||
import BadgeHelp from '@/components/info/BadgeHelp';
|
||||
import MiniButton from '@/components/ui/MiniButton';
|
||||
import { HelpTopic } from '@/models/miscellaneous';
|
||||
|
@ -12,6 +24,8 @@ import { useOssEdit } from '../OssEditContext';
|
|||
interface ToolbarOssGraphProps {
|
||||
isModified: boolean;
|
||||
showGrid: boolean;
|
||||
edgeAnimate: boolean;
|
||||
edgeStraight: boolean;
|
||||
onCreate: () => void;
|
||||
onDelete: () => void;
|
||||
onFitView: () => void;
|
||||
|
@ -19,81 +33,108 @@ interface ToolbarOssGraphProps {
|
|||
onSavePositions: () => void;
|
||||
onResetPositions: () => void;
|
||||
toggleShowGrid: () => void;
|
||||
toggleEdgeAnimate: () => void;
|
||||
toggleEdgeStraight: () => void;
|
||||
}
|
||||
|
||||
function ToolbarOssGraph({
|
||||
isModified,
|
||||
showGrid,
|
||||
edgeAnimate,
|
||||
edgeStraight,
|
||||
onCreate,
|
||||
onDelete,
|
||||
onFitView,
|
||||
onSaveImage,
|
||||
onSavePositions,
|
||||
onResetPositions,
|
||||
toggleShowGrid
|
||||
toggleShowGrid,
|
||||
toggleEdgeAnimate,
|
||||
toggleEdgeStraight
|
||||
}: ToolbarOssGraphProps) {
|
||||
const controller = useOssEdit();
|
||||
|
||||
return (
|
||||
<div className='cc-icons'>
|
||||
{controller.isMutable ? (
|
||||
<div className='flex flex-col items-center'>
|
||||
<div className='cc-icons'>
|
||||
<MiniButton
|
||||
titleHtml={prepareTooltip('Сохранить изменения', 'Ctrl + S')}
|
||||
icon={<IconSave size='1.25rem' className='icon-primary' />}
|
||||
disabled={controller.isProcessing || !isModified}
|
||||
onClick={onSavePositions}
|
||||
icon={<IconFitImage size='1.25rem' className='icon-primary' />}
|
||||
title='Сбросить вид'
|
||||
onClick={onFitView}
|
||||
/>
|
||||
) : null}
|
||||
{controller.isMutable ? (
|
||||
<MiniButton
|
||||
title='Сбросить изменения'
|
||||
icon={<IconReset size='1.25rem' className='icon-primary' />}
|
||||
disabled={!isModified}
|
||||
onClick={onResetPositions}
|
||||
title={showGrid ? 'Скрыть сетку' : 'Отобразить сетку'}
|
||||
icon={
|
||||
showGrid ? (
|
||||
<IconGrid size='1.25rem' className='icon-green' />
|
||||
) : (
|
||||
<IconGrid size='1.25rem' className='icon-primary' />
|
||||
)
|
||||
}
|
||||
onClick={toggleShowGrid}
|
||||
/>
|
||||
) : null}
|
||||
<MiniButton
|
||||
icon={<IconFitImage size='1.25rem' className='icon-primary' />}
|
||||
title='Сбросить вид'
|
||||
onClick={onFitView}
|
||||
/>
|
||||
<MiniButton
|
||||
title={showGrid ? 'Скрыть сетку' : 'Отобразить сетку'}
|
||||
icon={
|
||||
showGrid ? (
|
||||
<IconGrid size='1.25rem' className='icon-green' />
|
||||
) : (
|
||||
<IconGrid size='1.25rem' className='icon-primary' />
|
||||
)
|
||||
}
|
||||
onClick={toggleShowGrid}
|
||||
/>
|
||||
{controller.isMutable ? (
|
||||
<MiniButton
|
||||
title='Новая операция'
|
||||
icon={<IconNewItem size='1.25rem' className='icon-green' />}
|
||||
disabled={controller.isProcessing}
|
||||
onClick={onCreate}
|
||||
title={edgeStraight ? 'Связи: прямые' : 'Связи: безье'}
|
||||
icon={
|
||||
edgeStraight ? (
|
||||
<IconLineStraight size='1.25rem' className='icon-primary' />
|
||||
) : (
|
||||
<IconLineWave size='1.25rem' className='icon-primary' />
|
||||
)
|
||||
}
|
||||
onClick={toggleEdgeStraight}
|
||||
/>
|
||||
) : null}
|
||||
{controller.isMutable ? (
|
||||
<MiniButton
|
||||
title='Удалить выбранную'
|
||||
icon={<IconDestroy size='1.25rem' className='icon-red' />}
|
||||
disabled={controller.selected.length !== 1 || controller.isProcessing}
|
||||
onClick={onDelete}
|
||||
title={edgeAnimate ? 'Анимация: вкл' : 'Анимация: выкл'}
|
||||
icon={
|
||||
edgeAnimate ? (
|
||||
<IconAnimation size='1.25rem' className='icon-primary' />
|
||||
) : (
|
||||
<IconAnimationOff size='1.25rem' className='icon-primary' />
|
||||
)
|
||||
}
|
||||
onClick={toggleEdgeAnimate}
|
||||
/>
|
||||
<MiniButton
|
||||
icon={<IconImage size='1.25rem' className='icon-primary' />}
|
||||
title='Сохранить изображение'
|
||||
onClick={onSaveImage}
|
||||
/>
|
||||
<BadgeHelp
|
||||
topic={HelpTopic.UI_OSS_GRAPH}
|
||||
className={clsx(PARAMETER.TOOLTIP_WIDTH, 'sm:max-w-[40rem]')}
|
||||
offset={4}
|
||||
/>
|
||||
</div>
|
||||
{controller.isMutable ? (
|
||||
<div className='cc-icons'>
|
||||
{' '}
|
||||
<MiniButton
|
||||
titleHtml={prepareTooltip('Сохранить изменения', 'Ctrl + S')}
|
||||
icon={<IconSave size='1.25rem' className='icon-primary' />}
|
||||
disabled={controller.isProcessing || !isModified}
|
||||
onClick={onSavePositions}
|
||||
/>
|
||||
<MiniButton
|
||||
title='Сбросить изменения'
|
||||
icon={<IconReset size='1.25rem' className='icon-primary' />}
|
||||
disabled={!isModified}
|
||||
onClick={onResetPositions}
|
||||
/>
|
||||
<MiniButton
|
||||
title='Новая операция'
|
||||
icon={<IconNewItem size='1.25rem' className='icon-green' />}
|
||||
disabled={controller.isProcessing}
|
||||
onClick={onCreate}
|
||||
/>
|
||||
<MiniButton
|
||||
title='Удалить выбранную'
|
||||
icon={<IconDestroy size='1.25rem' className='icon-red' />}
|
||||
disabled={controller.selected.length !== 1 || controller.isProcessing}
|
||||
onClick={onDelete}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
<MiniButton
|
||||
icon={<IconImage size='1.25rem' className='icon-primary' />}
|
||||
title='Сохранить изображение'
|
||||
onClick={onSaveImage}
|
||||
/>
|
||||
<BadgeHelp
|
||||
topic={HelpTopic.UI_OSS_GRAPH}
|
||||
className={clsx(PARAMETER.TOOLTIP_WIDTH, 'sm:max-w-[40rem]')}
|
||||
offset={4}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -114,6 +114,8 @@ export const storage = {
|
|||
rsgraphFoldHidden: 'rsgraph.fold_hidden',
|
||||
|
||||
ossShowGrid: 'oss.show_grid',
|
||||
ossEdgeStraight: 'oss.edge_straight',
|
||||
ossEdgeAnimate: 'oss.edge_animate',
|
||||
|
||||
cstFilterMatch: 'cst.filter.match',
|
||||
cstFilterGraph: 'cst.filter.graph'
|
||||
|
|
Loading…
Reference in New Issue
Block a user