F: Improve RSForm UI for inherited cst
This commit is contained in:
parent
e5977d259b
commit
967eabde51
|
@ -253,7 +253,7 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
|
|||
|
||||
if operation.result is not None:
|
||||
can_edit = permissions.can_edit_item(request.user, operation.result)
|
||||
if can_edit:
|
||||
if can_edit or operation.operation_type == m.OperationType.SYNTHESIS:
|
||||
operation.result.alias = operation.alias
|
||||
operation.result.title = operation.title
|
||||
operation.result.comment = operation.comment
|
||||
|
|
|
@ -63,6 +63,8 @@ export { IoLibrary as IconLibrary2 } from 'react-icons/io5';
|
|||
export { BiDiamond as IconTemplates } from 'react-icons/bi';
|
||||
export { GiHoneycomb as IconOSS } from 'react-icons/gi';
|
||||
export { LuBaby as IconChild } from 'react-icons/lu';
|
||||
export { RiParentLine as IconParent } from 'react-icons/ri';
|
||||
export { TbOld as IconPredecessor } from 'react-icons/tb';
|
||||
export { RiHexagonLine as IconRSForm } from 'react-icons/ri';
|
||||
export { LuArchive as IconArchive } from 'react-icons/lu';
|
||||
export { LuDatabase as IconDatabase } from 'react-icons/lu';
|
||||
|
|
|
@ -110,6 +110,12 @@ function PickSubstitutions({
|
|||
toast.error(errors.reuseOriginal);
|
||||
return;
|
||||
}
|
||||
if (leftArgument === rightArgument) {
|
||||
if ((deleteRight && rightCst?.is_inherited) || (!deleteRight && leftCst?.is_inherited)) {
|
||||
toast.error(errors.substituteInherited);
|
||||
return;
|
||||
}
|
||||
}
|
||||
setSubstitutions(prev => [...prev, newSubstitution]);
|
||||
setLeftCst(undefined);
|
||||
setRightCst(undefined);
|
||||
|
|
|
@ -32,7 +32,7 @@ function SelectConstituenta({
|
|||
return (
|
||||
items?.map(cst => ({
|
||||
value: cst.id,
|
||||
label: `${cst.alias}: ${describeConstituenta(cst)}`
|
||||
label: `${cst.alias}${cst.is_inherited ? '*' : ''}: ${describeConstituenta(cst)}`
|
||||
})) ?? []
|
||||
);
|
||||
}, [items]);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { HelpTopic } from '@/models/miscellaneous';
|
||||
|
||||
import LinkTopic from '../../../components/ui/LinkTopic';
|
||||
import LinkTopic from '@/components/ui/LinkTopic';
|
||||
|
||||
function HelpCstAttributes() {
|
||||
return (
|
||||
|
|
|
@ -74,7 +74,6 @@ function HelpCstEditor() {
|
|||
<IconMoveUp className='inline-icon' /> Alt + вверх/вниз – перемещение
|
||||
</li>
|
||||
<li>фильтрация в верхней части</li>
|
||||
<li>при наведении на имя конституенты отображаются атрибуты</li>
|
||||
<li>
|
||||
<span style={{ backgroundColor: colors.bgSelected }}>цветом фона</span> выделена текущая конституента
|
||||
</li>
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { urls } from '@/app/urls';
|
||||
import { IconLibrary2, IconManuals, IconUser2 } from '@/components/Icons';
|
||||
import LinkTopic from '@/components/ui/LinkTopic';
|
||||
import TextURL from '@/components/ui/TextURL';
|
||||
import { HelpTopic } from '@/models/miscellaneous';
|
||||
import { external_urls, prefixes } from '@/utils/constants';
|
||||
|
||||
import LinkTopic from '../../../components/ui/LinkTopic';
|
||||
import TopicItem from '../TopicItem';
|
||||
|
||||
function HelpPortal() {
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import { HelpTopic } from '@/models/miscellaneous';
|
||||
|
||||
import {
|
||||
IconClone,
|
||||
IconDestroy,
|
||||
|
@ -10,8 +8,9 @@ import {
|
|||
IconOwner,
|
||||
IconPublic,
|
||||
IconSave
|
||||
} from '../../../components/Icons';
|
||||
import LinkTopic from '../../../components/ui/LinkTopic';
|
||||
} from '@/components/Icons';
|
||||
import LinkTopic from '@/components/ui/LinkTopic';
|
||||
import { HelpTopic } from '@/models/miscellaneous';
|
||||
|
||||
function HelpRSFormCard() {
|
||||
return (
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
import InfoCstStatus from '@/components/info/InfoCstStatus';
|
||||
import Divider from '@/components/ui/Divider';
|
||||
import { HelpTopic } from '@/models/miscellaneous';
|
||||
|
||||
import {
|
||||
IconAlias,
|
||||
IconClone,
|
||||
|
@ -11,17 +7,24 @@ import {
|
|||
IconNewItem,
|
||||
IconOpenList,
|
||||
IconReset
|
||||
} from '../../../components/Icons';
|
||||
import LinkTopic from '../../../components/ui/LinkTopic';
|
||||
} from '@/components/Icons';
|
||||
import InfoCstStatus from '@/components/info/InfoCstStatus';
|
||||
import Divider from '@/components/ui/Divider';
|
||||
import LinkTopic from '@/components/ui/LinkTopic';
|
||||
import { HelpTopic } from '@/models/miscellaneous';
|
||||
|
||||
function HelpRSFormItems() {
|
||||
return (
|
||||
<div className='dense'>
|
||||
<h1>Список конституент</h1>
|
||||
<p>
|
||||
<li>
|
||||
<IconAlias className='inline-icon' />
|
||||
Конституенты обладают уникальным <LinkTopic text='Именем' topic={HelpTopic.CC_CONSTITUENTA} />
|
||||
</p>
|
||||
</li>
|
||||
<li>при наведении на имя отображаются атрибуты</li>
|
||||
<li>
|
||||
пунктиром отображаются <LinkTopic text='наследованные' topic={HelpTopic.CC_OSS} /> конституенты
|
||||
</li>
|
||||
|
||||
<h2>Управление списком</h2>
|
||||
<li>
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import LinkTopic from '@/components/ui/LinkTopic';
|
||||
import { HelpTopic } from '@/models/miscellaneous';
|
||||
|
||||
import LinkTopic from '../../../components/ui/LinkTopic';
|
||||
|
||||
function HelpRSLangCorrect() {
|
||||
return (
|
||||
<div className='text-justify'>
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import LinkTopic from '@/components/ui/LinkTopic';
|
||||
import { HelpTopic } from '@/models/miscellaneous';
|
||||
|
||||
import LinkTopic from '../../../components/ui/LinkTopic';
|
||||
|
||||
function HelpRSLangInterpret() {
|
||||
return (
|
||||
<div className='text-justify'>
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import { HelpTopic } from '@/models/miscellaneous';
|
||||
|
||||
import {
|
||||
IconGenerateNames,
|
||||
IconGenerateStructure,
|
||||
|
@ -7,8 +5,9 @@ import {
|
|||
IconReplace,
|
||||
IconSortList,
|
||||
IconTemplates
|
||||
} from '../../../components/Icons';
|
||||
import LinkTopic from '../../../components/ui/LinkTopic';
|
||||
} from '@/components/Icons';
|
||||
import LinkTopic from '@/components/ui/LinkTopic';
|
||||
import { HelpTopic } from '@/models/miscellaneous';
|
||||
|
||||
function HelpRSLangOperations() {
|
||||
return (
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
import Divider from '@/components/ui/Divider';
|
||||
import LinkTopic from '@/components/ui/LinkTopic';
|
||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
||||
import { HelpTopic } from '@/models/miscellaneous';
|
||||
|
||||
import {
|
||||
IconClustering,
|
||||
IconDestroy,
|
||||
|
@ -20,7 +15,11 @@ import {
|
|||
IconReset,
|
||||
IconRotate3D,
|
||||
IconText
|
||||
} from '../../../components/Icons';
|
||||
} from '@/components/Icons';
|
||||
import Divider from '@/components/ui/Divider';
|
||||
import LinkTopic from '@/components/ui/LinkTopic';
|
||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
||||
import { HelpTopic } from '@/models/miscellaneous';
|
||||
|
||||
function HelpTermGraph() {
|
||||
const { colors } = useConceptOptions();
|
||||
|
@ -80,6 +79,9 @@ function HelpTermGraph() {
|
|||
<li>
|
||||
<IconImage className='inline-icon' /> Сохранить в формат PNG
|
||||
</li>
|
||||
<li>
|
||||
* <LinkTopic text='наследованные' topic={HelpTopic.CC_OSS} /> в ОСС
|
||||
</li>
|
||||
</div>
|
||||
|
||||
<Divider vertical margins='mx-3' className='hidden sm:block' />
|
||||
|
|
|
@ -89,7 +89,7 @@ function EditorConstituenta({ activeCst, isModified, setIsModified, onOpenEdit }
|
|||
onMoveDown={controller.moveDown}
|
||||
onSubmit={initiateSubmit}
|
||||
onReset={() => setToggleReset(prev => !prev)}
|
||||
onDelete={controller.deleteCst}
|
||||
onDelete={controller.promptDeleteCst}
|
||||
onClone={controller.cloneCst}
|
||||
onCreate={() => controller.createCst(activeCst?.cst_type, false)}
|
||||
onToggleList={() => setShowList(prev => !prev)}
|
||||
|
|
|
@ -5,7 +5,7 @@ import { AnimatePresence } from 'framer-motion';
|
|||
import { useEffect, useLayoutEffect, useMemo, useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
import { IconChild, IconSave } from '@/components/Icons';
|
||||
import { IconChild, IconParent, IconSave } from '@/components/Icons';
|
||||
import RefsInput from '@/components/RefsInput';
|
||||
import MiniButton from '@/components/ui/MiniButton';
|
||||
import Overlay from '@/components/ui/Overlay';
|
||||
|
@ -241,15 +241,22 @@ function FormConstituenta({
|
|||
disabled={disabled || !isModified}
|
||||
icon={<IconSave size='1.25rem' />}
|
||||
/>
|
||||
{state?.is_inherited ? (
|
||||
<Overlay position='right-[-2rem]'>
|
||||
<Overlay position='top-[0.1rem] left-[0.4rem]' className='cc-icons'>
|
||||
{state?.is_inherited_parent ? (
|
||||
<MiniButton
|
||||
icon={<IconChild size='1.25rem' className='clr-text-red' />}
|
||||
disabled
|
||||
titleHtml='Внимание!</br> Конституента имеет потомков<br/> в операционной схеме синтеза'
|
||||
/>
|
||||
</Overlay>
|
||||
) : null}
|
||||
) : null}
|
||||
{state?.is_inherited ? (
|
||||
<MiniButton
|
||||
icon={<IconParent size='1.25rem' className='clr-text-red' />}
|
||||
disabled
|
||||
titleHtml='Внимание!</br> Конституента является наследником<br/>'
|
||||
/>
|
||||
) : null}
|
||||
</Overlay>
|
||||
</div>
|
||||
) : null}
|
||||
</AnimatePresence>
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
'use client';
|
||||
|
||||
import {
|
||||
IconClone,
|
||||
IconDestroy,
|
||||
|
@ -16,6 +18,8 @@ import { HelpTopic } from '@/models/miscellaneous';
|
|||
import { PARAMETER } from '@/utils/constants';
|
||||
import { prepareTooltip, tooltips } from '@/utils/labels';
|
||||
|
||||
import { useRSEdit } from '../RSEditContext';
|
||||
|
||||
interface ToolbarConstituentaProps {
|
||||
disabled: boolean;
|
||||
modified: boolean;
|
||||
|
@ -46,6 +50,8 @@ function ToolbarConstituenta({
|
|||
onCreate,
|
||||
onToggleList
|
||||
}: ToolbarConstituentaProps) {
|
||||
const controller = useRSEdit();
|
||||
|
||||
return (
|
||||
<Overlay position='top-1 right-4' className='cc-icons sm:right-1/2 sm:translate-x-1/2'>
|
||||
<MiniButton
|
||||
|
@ -74,7 +80,7 @@ function ToolbarConstituenta({
|
|||
/>
|
||||
<MiniButton
|
||||
title='Удалить редактируемую конституенту'
|
||||
disabled={disabled}
|
||||
disabled={disabled || !controller.canDeleteSelected}
|
||||
onClick={onDelete}
|
||||
icon={<IconDestroy size='1.25rem' className='icon-red' />}
|
||||
/>
|
||||
|
|
|
@ -15,7 +15,7 @@ import { UserID, UserLevel } from '@/models/user';
|
|||
import { prefixes } from '@/utils/constants';
|
||||
import { prompts } from '@/utils/labels';
|
||||
|
||||
import LabeledValue from '../../../components/ui/LabeledValue';
|
||||
import LabeledValue from '@/components/ui/LabeledValue';
|
||||
|
||||
interface EditorLibraryItemProps {
|
||||
item?: ILibraryItemData;
|
||||
|
|
|
@ -105,6 +105,7 @@ function FormRSForm({ id, isModified, setIsModified }: FormRSFormProps) {
|
|||
onClick={() => ossMenu.toggle()}
|
||||
/>
|
||||
<Dropdown isOpen={ossMenu.isOpen} className='mt-[-0.1rem]'>
|
||||
<Label text='Список ОСС' className='border-b px-3' />
|
||||
{schema.oss.map((reference, index) => (
|
||||
<DropdownButton
|
||||
className='min-w-[5rem]'
|
||||
|
@ -145,7 +146,7 @@ function FormRSForm({ id, isModified, setIsModified }: FormRSFormProps) {
|
|||
onChange={event => setAlias(event.target.value)}
|
||||
/>
|
||||
<div className='flex flex-col'>
|
||||
<ToolbarVersioning />
|
||||
<ToolbarVersioning blockReload={schema && schema?.oss.length > 0} />
|
||||
<ToolbarItemAccess
|
||||
visible={visible}
|
||||
toggleVisible={() => setVisible(prev => !prev)}
|
||||
|
|
|
@ -7,7 +7,11 @@ import { PARAMETER } from '@/utils/constants';
|
|||
|
||||
import { useRSEdit } from '../RSEditContext';
|
||||
|
||||
function ToolbarVersioning() {
|
||||
interface ToolbarVersioningProps {
|
||||
blockReload?: boolean;
|
||||
}
|
||||
|
||||
function ToolbarVersioning({ blockReload }: ToolbarVersioningProps) {
|
||||
const controller = useRSEdit();
|
||||
return (
|
||||
<Overlay position='top-[-0.4rem] right-[0rem]' className='pr-2 cc-icons'>
|
||||
|
@ -15,9 +19,13 @@ function ToolbarVersioning() {
|
|||
<>
|
||||
<MiniButton
|
||||
titleHtml={
|
||||
!controller.isContentEditable ? 'Откатить к версии' : 'Переключитесь на <br/>неактуальную версию'
|
||||
blockReload
|
||||
? 'Невозможно откатить КС, прикрепленную к операционной схеме'
|
||||
: !controller.isContentEditable
|
||||
? 'Откатить к версии'
|
||||
: 'Переключитесь на <br/>неактуальную версию'
|
||||
}
|
||||
disabled={controller.isContentEditable}
|
||||
disabled={controller.isContentEditable || blockReload}
|
||||
onClick={() => controller.restoreVersion()}
|
||||
icon={<IconUpload size='1.25rem' className='icon-red' />}
|
||||
/>
|
||||
|
@ -30,7 +38,7 @@ function ToolbarVersioning() {
|
|||
<MiniButton
|
||||
title={controller.schema?.versions.length === 0 ? 'Список версий пуст' : 'Редактировать версии'}
|
||||
disabled={!controller.schema || controller.schema?.versions.length === 0}
|
||||
onClick={controller.editVersions}
|
||||
onClick={controller.promptEditVersions}
|
||||
icon={<IconVersions size='1.25rem' className='icon-primary' />}
|
||||
/>
|
||||
</>
|
||||
|
|
|
@ -59,10 +59,10 @@ function EditorRSList({ onOpenEdit }: EditorRSListProps) {
|
|||
if (!controller.isContentEditable || controller.isProcessing) {
|
||||
return;
|
||||
}
|
||||
if (event.key === 'Delete' && controller.selected.length > 0) {
|
||||
if (event.key === 'Delete' && controller.canDeleteSelected) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
controller.deleteCst();
|
||||
controller.promptDeleteCst();
|
||||
return;
|
||||
}
|
||||
if (!event.altKey || event.shiftKey) {
|
||||
|
|
|
@ -79,8 +79,8 @@ function ToolbarRSList() {
|
|||
<MiniButton
|
||||
titleHtml={prepareTooltip('Удалить выбранные', 'Delete')}
|
||||
icon={<IconDestroy size='1.25rem' className='icon-red' />}
|
||||
disabled={controller.isProcessing || controller.nothingSelected}
|
||||
onClick={controller.deleteCst}
|
||||
disabled={controller.isProcessing || !controller.canDeleteSelected}
|
||||
onClick={controller.promptDeleteCst}
|
||||
/>
|
||||
<BadgeHelp topic={HelpTopic.UI_RS_LIST} offset={5} />
|
||||
</Overlay>
|
||||
|
|
|
@ -105,7 +105,7 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) {
|
|||
result.push({
|
||||
id: String(node.id),
|
||||
fill: focusCst === cst ? colors.bgPurple : colorBgGraphNode(cst, coloring, colors),
|
||||
label: cst.alias,
|
||||
label: `${cst.alias}${cst.is_inherited ? '*' : ''}`,
|
||||
subLabel: !filterParams.noText ? cst.term_resolved : undefined,
|
||||
size: applyNodeSizing(cst, sizing)
|
||||
});
|
||||
|
@ -141,10 +141,10 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) {
|
|||
}
|
||||
|
||||
function handleDeleteCst() {
|
||||
if (!controller.schema || controller.nothingSelected) {
|
||||
if (!controller.schema || !controller.canDeleteSelected) {
|
||||
return;
|
||||
}
|
||||
controller.deleteCst();
|
||||
controller.promptDeleteCst();
|
||||
}
|
||||
|
||||
const handleChangeLayout = useCallback(
|
||||
|
|
|
@ -105,7 +105,7 @@ function ToolbarTermGraph({
|
|||
<MiniButton
|
||||
title='Удалить выбранные'
|
||||
icon={<IconDestroy size='1.25rem' className='icon-red' />}
|
||||
disabled={controller.nothingSelected || controller.isProcessing}
|
||||
disabled={!controller.canDeleteSelected || controller.isProcessing}
|
||||
onClick={onDelete}
|
||||
/>
|
||||
) : null}
|
||||
|
|
|
@ -105,12 +105,19 @@ function ViewHidden({ items, selected, toggleSelection, setFocus, schema, colori
|
|||
className='min-w-[3rem] rounded-md text-center select-none'
|
||||
style={{
|
||||
backgroundColor: colorBgGraphNode(cst, adjustedColoring, colors),
|
||||
...(localSelected.includes(cstID) ? { outlineWidth: '2px', outlineStyle: 'solid' } : {})
|
||||
...(localSelected.includes(cstID)
|
||||
? {
|
||||
outlineWidth: '2px',
|
||||
outlineStyle: cst.is_inherited ? 'dashed' : 'solid',
|
||||
outlineColor: colors.fgDefault
|
||||
}
|
||||
: {})
|
||||
}}
|
||||
onClick={event => handleClick(cstID, event)}
|
||||
onDoubleClick={() => onEdit(cstID)}
|
||||
>
|
||||
{cst.alias}
|
||||
{cst.is_inherited ? '*' : ''}
|
||||
</button>
|
||||
<TooltipConstituenta data={cst} anchor={`#${id}`} />
|
||||
</div>
|
||||
|
|
|
@ -62,6 +62,7 @@ export interface IRSEditContext {
|
|||
isProcessing: boolean;
|
||||
canProduceStructure: boolean;
|
||||
nothingSelected: boolean;
|
||||
canDeleteSelected: boolean;
|
||||
|
||||
updateSchema: (data: ILibraryUpdateData) => void;
|
||||
|
||||
|
@ -81,14 +82,14 @@ export interface IRSEditContext {
|
|||
viewVersion: (version?: VersionID, newTab?: boolean) => void;
|
||||
createVersion: () => void;
|
||||
restoreVersion: () => void;
|
||||
editVersions: () => void;
|
||||
promptEditVersions: () => void;
|
||||
|
||||
moveUp: () => void;
|
||||
moveDown: () => void;
|
||||
createCst: (type: CstType | undefined, skipDialog: boolean, definition?: string) => void;
|
||||
renameCst: () => void;
|
||||
cloneCst: () => void;
|
||||
deleteCst: () => void;
|
||||
promptDeleteCst: () => void;
|
||||
editTermForms: () => void;
|
||||
|
||||
promptTemplate: () => void;
|
||||
|
@ -145,6 +146,10 @@ export const RSEditState = ({
|
|||
);
|
||||
const isContentEditable = useMemo(() => isMutable && !model.isArchive, [isMutable, model.isArchive]);
|
||||
const nothingSelected = useMemo(() => selected.length === 0, [selected]);
|
||||
const canDeleteSelected = useMemo(
|
||||
() => !nothingSelected && selected.every(id => !model.schema?.cstByID.get(id)?.is_inherited),
|
||||
[selected, nothingSelected, model.schema]
|
||||
);
|
||||
|
||||
const [showUpload, setShowUpload] = useState(false);
|
||||
const [showClone, setShowClone] = useState(false);
|
||||
|
@ -598,6 +603,7 @@ export const RSEditState = ({
|
|||
isProcessing: model.processing,
|
||||
canProduceStructure,
|
||||
nothingSelected,
|
||||
canDeleteSelected,
|
||||
|
||||
toggleSubscribe,
|
||||
setOwner,
|
||||
|
@ -616,14 +622,14 @@ export const RSEditState = ({
|
|||
viewVersion,
|
||||
createVersion,
|
||||
restoreVersion,
|
||||
editVersions: () => setShowEditVersions(true),
|
||||
promptEditVersions: () => setShowEditVersions(true),
|
||||
|
||||
moveUp,
|
||||
moveDown,
|
||||
createCst,
|
||||
cloneCst,
|
||||
renameCst,
|
||||
deleteCst: () => setShowDeleteCst(true),
|
||||
promptDeleteCst: () => setShowDeleteCst(true),
|
||||
editTermForms,
|
||||
|
||||
promptTemplate,
|
||||
|
|
|
@ -952,7 +952,8 @@ export const errors = {
|
|||
astFailed: 'Невозможно построить дерево разбора',
|
||||
passwordsMismatch: 'Пароли не совпадают',
|
||||
imageFailed: 'Ошибка при создании изображения',
|
||||
reuseOriginal: 'Повторное использование удаляемой конституенты при отождествлении'
|
||||
reuseOriginal: 'Повторное использование удаляемой конституенты при отождествлении',
|
||||
substituteInherited: 'Нельзя удалять наследованные конституенты при отождествлении'
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue
Block a user