Refactor help topics UI

This commit is contained in:
IRBorisov 2024-05-15 02:51:50 +03:00
parent 22ef6b1886
commit 114f5d75cc
16 changed files with 258 additions and 75 deletions

View File

@ -34,14 +34,14 @@ function InfoTopic({ topic }: InfoTopicProps) {
if (topic === HelpTopic.MAIN) return <HelpMain />; if (topic === HelpTopic.MAIN) return <HelpMain />;
if (topic === HelpTopic.INTERFACE) return <HelpInterface />; if (topic === HelpTopic.INTERFACE) return <HelpInterface />;
if (topic === HelpTopic.LIBRARY) return <HelpLibrary />; if (topic === HelpTopic.UI_LIBRARY) return <HelpLibrary />;
if (topic === HelpTopic.RSFORM_UI) return <HelpRSFormUI />; if (topic === HelpTopic.UI_RSFORM) return <HelpRSFormUI />;
if (topic === HelpTopic.RSFORM_CARD) return <HelpRSFormMeta />; if (topic === HelpTopic.UI_RSFORM_CARD) return <HelpRSFormMeta />;
if (topic === HelpTopic.RSFORM_LIST) return <HelpRSFormItems />; if (topic === HelpTopic.UI_RSFORM_LIST) return <HelpRSFormItems />;
if (topic === HelpTopic.RSFORM_EDITOR) return <HelpCstEditor />; if (topic === HelpTopic.UI_RSFORM_EDITOR) return <HelpCstEditor />;
if (topic === HelpTopic.GRAPH_TERM) return <HelpTermGraph />; if (topic === HelpTopic.UI_GRAPH_TERM) return <HelpTermGraph />;
if (topic === HelpTopic.CST_STATUS) return <HelpCstStatus />; if (topic === HelpTopic.UI_CST_STATUS) return <HelpCstStatus />;
if (topic === HelpTopic.CST_CLASS) return <HelpCstClass />; if (topic === HelpTopic.UI_CST_CLASS) return <HelpCstClass />;
if (topic === HelpTopic.CONCEPTUAL) return <HelpConcept />; if (topic === HelpTopic.CONCEPTUAL) return <HelpConcept />;
if (topic === HelpTopic.CC_SYSTEM) return <HelpConceptSystem />; if (topic === HelpTopic.CC_SYSTEM) return <HelpConceptSystem />;

View File

@ -45,14 +45,14 @@ export enum HelpTopic {
MAIN = 'main', MAIN = 'main',
INTERFACE = 'user-interface', INTERFACE = 'user-interface',
LIBRARY = 'ui-library', UI_LIBRARY = 'ui-library',
RSFORM_UI = 'ui-rsform', UI_RSFORM = 'ui-rsform',
RSFORM_CARD = 'ui-rsform-card', UI_RSFORM_CARD = 'ui-rsform-card',
RSFORM_LIST = 'ui-rsform-list', UI_RSFORM_LIST = 'ui-rsform-list',
RSFORM_EDITOR = 'ui-rsform-editor', UI_RSFORM_EDITOR = 'ui-rsform-editor',
GRAPH_TERM = 'ui-rsform-graph', UI_GRAPH_TERM = 'ui-rsform-graph',
CST_STATUS = 'ui-rsform-cst-status', UI_CST_STATUS = 'ui-rsform-cst-status',
CST_CLASS = 'ui-rsform-cst-class', UI_CST_CLASS = 'ui-rsform-cst-class',
CONCEPTUAL = 'concept', CONCEPTUAL = 'concept',
CC_SYSTEM = 'rslang-rsform', CC_SYSTEM = 'rslang-rsform',
@ -73,6 +73,46 @@ export enum HelpTopic {
PRIVACY = 'privacy' PRIVACY = 'privacy'
} }
/**
* Manual topics hierarchy.
*/
export const topicParent: Map<HelpTopic, HelpTopic> = new Map([
[HelpTopic.MAIN, HelpTopic.MAIN],
[HelpTopic.INTERFACE, HelpTopic.INTERFACE],
[HelpTopic.UI_LIBRARY, HelpTopic.INTERFACE],
[HelpTopic.UI_RSFORM, HelpTopic.INTERFACE],
[HelpTopic.UI_RSFORM_CARD, HelpTopic.UI_RSFORM],
[HelpTopic.UI_RSFORM_LIST, HelpTopic.UI_RSFORM],
[HelpTopic.UI_RSFORM_EDITOR, HelpTopic.UI_RSFORM],
[HelpTopic.UI_GRAPH_TERM, HelpTopic.UI_RSFORM],
[HelpTopic.UI_CST_STATUS, HelpTopic.UI_RSFORM],
[HelpTopic.UI_CST_CLASS, HelpTopic.UI_RSFORM],
[HelpTopic.CONCEPTUAL, HelpTopic.CONCEPTUAL],
[HelpTopic.CC_SYSTEM, HelpTopic.CONCEPTUAL],
[HelpTopic.CC_CONSTITUENTA, HelpTopic.CONCEPTUAL],
[HelpTopic.CC_RELATIONS, HelpTopic.CONCEPTUAL],
[HelpTopic.RSLANG, HelpTopic.RSLANG],
[HelpTopic.RSL_TYPES, HelpTopic.RSLANG],
[HelpTopic.RSL_CORRECT, HelpTopic.RSLANG],
[HelpTopic.RSL_INTERPRET, HelpTopic.RSLANG],
[HelpTopic.RSL_TEMPLATES, HelpTopic.RSLANG],
[HelpTopic.RSL_OPERATIONS, HelpTopic.RSLANG],
[HelpTopic.TERM_CONTROL, HelpTopic.TERM_CONTROL],
[HelpTopic.VERSIONS, HelpTopic.VERSIONS],
[HelpTopic.EXTEOR, HelpTopic.EXTEOR],
[HelpTopic.API, HelpTopic.API],
[HelpTopic.PRIVACY, HelpTopic.PRIVACY]
]);
/**
* Topics that can be folded.
*/
export const foldableTopics = [HelpTopic.INTERFACE, HelpTopic.UI_RSFORM, HelpTopic.RSLANG];
/** /**
* Represents {@link IConstituenta} matching mode. * Represents {@link IConstituenta} matching mode.
*/ */

View File

@ -124,7 +124,7 @@ function ViewLibrary({ items, resetQuery }: ViewLibraryProps) {
'flex gap-1' 'flex gap-1'
)} )}
> >
<BadgeHelp topic={HelpTopic.LIBRARY} className='max-w-[30rem] text-sm' offset={5} place='right-start' /> <BadgeHelp topic={HelpTopic.UI_LIBRARY} className='max-w-[30rem] text-sm' offset={5} place='right-start' />
</div> </div>
</div> </div>
<DataTable <DataTable

View File

@ -1,10 +1,12 @@
'use client'; 'use client';
import { useCallback, useState } from 'react';
import { urls } from '@/app/urls'; import { urls } from '@/app/urls';
import { useConceptNavigation } from '@/context/NavigationContext'; import { useConceptNavigation } from '@/context/NavigationContext';
import { useConceptOptions } from '@/context/OptionsContext'; import { useConceptOptions } from '@/context/OptionsContext';
import useQueryStrings from '@/hooks/useQueryStrings'; import useQueryStrings from '@/hooks/useQueryStrings';
import { HelpTopic } from '@/models/miscellaneous'; import { HelpTopic, topicParent } from '@/models/miscellaneous';
import TopicsList from './TopicsList'; import TopicsList from './TopicsList';
import ViewTopic from './ViewTopic'; import ViewTopic from './ViewTopic';
@ -12,18 +14,63 @@ import ViewTopic from './ViewTopic';
function ManualsPage() { function ManualsPage() {
const router = useConceptNavigation(); const router = useConceptNavigation();
const query = useQueryStrings(); const query = useQueryStrings();
const topic = (query.get('topic') || HelpTopic.MAIN) as HelpTopic; const activeTopic = (query.get('topic') || HelpTopic.MAIN) as HelpTopic;
const [topicFolded, setFolded] = useState<Map<HelpTopic, boolean>>(
new Map(
Object.values(HelpTopic).map(value => {
const topic = value as HelpTopic;
return [
topic,
topicParent.get(activeTopic) !== topic && topicParent.get(topicParent.get(activeTopic)!) !== topic
];
})
)
);
const { mainHeight } = useConceptOptions(); const { mainHeight } = useConceptOptions();
const onFoldTopic = useCallback(
(target: HelpTopic, showChildren: boolean) => {
if (topicFolded.get(target) === !showChildren) {
return;
}
setFolded(
new Map(
Object.values(HelpTopic).map(value => {
const topic = value as HelpTopic;
if (topic === target) {
return [topic, !showChildren];
}
if (
!showChildren &&
(topicParent.get(topic) === target || topicParent.get(topicParent.get(topic)!) === target)
) {
return [topic, true];
}
const oldValue = topicFolded.get(topic)!;
return [topic, oldValue];
})
)
);
},
[topicFolded]
);
function onSelectTopic(newTopic: HelpTopic) { const onSelectTopic = useCallback(
router.push(urls.help_topic(newTopic)); (newTopic: HelpTopic) => {
} router.push(urls.help_topic(newTopic));
},
[router]
);
return ( return (
<div className='flex w-full gap-2' style={{ minHeight: mainHeight }}> <div className='flex w-full gap-2' style={{ minHeight: mainHeight }}>
<TopicsList activeTopic={topic} onChangeTopic={topic => onSelectTopic(topic)} /> <TopicsList
<ViewTopic topic={topic} /> activeTopic={activeTopic}
onChangeTopic={topic => onSelectTopic(topic)}
topicFolded={topicFolded}
onFoldTopic={onFoldTopic}
/>
<ViewTopic topic={activeTopic} />
</div> </div>
); );
} }

View File

@ -15,10 +15,12 @@ import TopicsTree from './TopicsTree';
interface TopicsDropdownProps { interface TopicsDropdownProps {
activeTopic: HelpTopic; activeTopic: HelpTopic;
topicFolded: Map<HelpTopic, boolean>;
onChangeTopic: (newTopic: HelpTopic) => void; onChangeTopic: (newTopic: HelpTopic) => void;
onFoldTopic: (target: HelpTopic, showChildren: boolean) => void;
} }
function TopicsDropdown({ activeTopic, onChangeTopic }: TopicsDropdownProps) { function TopicsDropdown({ activeTopic, topicFolded, onChangeTopic, onFoldTopic }: TopicsDropdownProps) {
const menu = useDropdown(); const menu = useDropdown();
const { noNavigation, calculateHeight } = useConceptOptions(); const { noNavigation, calculateHeight } = useConceptOptions();
@ -34,7 +36,7 @@ function TopicsDropdown({ activeTopic, onChangeTopic }: TopicsDropdownProps) {
<div <div
ref={menu.ref} ref={menu.ref}
className={clsx( className={clsx(
'absolute left-0 w-[13rem]', // prettier: split-lines 'absolute left-0 w-[13.5rem]', // prettier: split-lines
'flex flex-col', 'flex flex-col',
'z-modal-tooltip', 'z-modal-tooltip',
'text-xs sm:text-sm', 'text-xs sm:text-sm',
@ -55,13 +57,22 @@ function TopicsDropdown({ activeTopic, onChangeTopic }: TopicsDropdownProps) {
onClick={menu.toggle} onClick={menu.toggle}
/> />
<motion.div <motion.div
className='border divide-y rounded-none cc-scroll-y' className={clsx(
'border divide-y rounded-none', // prettier: split-lines
'cc-scroll-y',
'clr-controls'
)}
style={{ maxHeight: calculateHeight('4rem + 2px') }} style={{ maxHeight: calculateHeight('4rem + 2px') }}
initial={false} initial={false}
animate={menu.isOpen ? 'open' : 'closed'} animate={menu.isOpen ? 'open' : 'closed'}
variants={animateSlideLeft} variants={animateSlideLeft}
> >
<TopicsTree activeTopic={activeTopic} onChangeTopic={selectTheme} /> <TopicsTree
activeTopic={activeTopic}
onChangeTopic={selectTheme}
topicFolded={topicFolded}
onFoldTopic={onFoldTopic}
/>
</motion.div> </motion.div>
</div> </div>
); );

View File

@ -8,16 +8,32 @@ import TopicsStatic from './TopicsStatic';
interface TopicsListProps { interface TopicsListProps {
activeTopic: HelpTopic; activeTopic: HelpTopic;
topicFolded: Map<HelpTopic, boolean>;
onChangeTopic: (newTopic: HelpTopic) => void; onChangeTopic: (newTopic: HelpTopic) => void;
onFoldTopic: (target: HelpTopic, showChildren: boolean) => void;
} }
function TopicsList({ activeTopic, onChangeTopic }: TopicsListProps) { function TopicsList({ activeTopic, topicFolded, onChangeTopic, onFoldTopic }: TopicsListProps) {
const size = useWindowSize(); const size = useWindowSize();
if (!size.isSmall) { if (!size.isSmall) {
return <TopicsStatic activeTopic={activeTopic} onChangeTopic={onChangeTopic} />; return (
<TopicsStatic
activeTopic={activeTopic}
onChangeTopic={onChangeTopic}
topicFolded={topicFolded}
onFoldTopic={onFoldTopic}
/>
);
} else { } else {
return <TopicsDropdown activeTopic={activeTopic} onChangeTopic={onChangeTopic} />; return (
<TopicsDropdown
activeTopic={activeTopic}
onChangeTopic={onChangeTopic}
topicFolded={topicFolded}
onFoldTopic={onFoldTopic}
/>
);
} }
} }

View File

@ -7,16 +7,18 @@ import TopicsTree from './TopicsTree';
interface TopicsStaticProps { interface TopicsStaticProps {
activeTopic: HelpTopic; activeTopic: HelpTopic;
topicFolded: Map<HelpTopic, boolean>;
onChangeTopic: (newTopic: HelpTopic) => void; onChangeTopic: (newTopic: HelpTopic) => void;
onFoldTopic: (target: HelpTopic, showChildren: boolean) => void;
} }
function TopicsStatic({ activeTopic, onChangeTopic }: TopicsStaticProps) { function TopicsStatic({ activeTopic, topicFolded, onChangeTopic, onFoldTopic }: TopicsStaticProps) {
const { calculateHeight } = useConceptOptions(); const { calculateHeight } = useConceptOptions();
return ( return (
<div <div
className={clsx( className={clsx(
'sticky top-0 left-0', 'sticky top-0 left-0',
'w-[14rem] cc-scroll-y', 'w-[14.5rem] cc-scroll-y',
'self-start', 'self-start',
'border divide-y rounded-none', 'border divide-y rounded-none',
'clr-controls', 'clr-controls',
@ -25,7 +27,12 @@ function TopicsStatic({ activeTopic, onChangeTopic }: TopicsStaticProps) {
)} )}
style={{ maxHeight: calculateHeight('2.25rem + 2px') }} style={{ maxHeight: calculateHeight('2.25rem + 2px') }}
> >
<TopicsTree activeTopic={activeTopic} onChangeTopic={onChangeTopic} /> <TopicsTree
activeTopic={activeTopic}
onChangeTopic={onChangeTopic}
topicFolded={topicFolded}
onFoldTopic={onFoldTopic}
/>
</div> </div>
); );
} }

View File

@ -1,35 +1,75 @@
'use client'; 'use client';
import clsx from 'clsx'; import clsx from 'clsx';
import { AnimatePresence } from 'framer-motion'; import { AnimatePresence, motion } from 'framer-motion';
import { useCallback } from 'react';
import { HelpTopic } from '@/models/miscellaneous'; import { IconDropArrow, IconPageRight } from '@/components/Icons';
import { CProps } from '@/components/props';
import MiniButton from '@/components/ui/MiniButton';
import Overlay from '@/components/ui/Overlay';
import { foldableTopics, HelpTopic, topicParent } from '@/models/miscellaneous';
import { animateSideAppear } from '@/styling/animations';
import { prefixes } from '@/utils/constants'; import { prefixes } from '@/utils/constants';
import { describeHelpTopic, labelHelpTopic } from '@/utils/labels'; import { describeHelpTopic, labelHelpTopic } from '@/utils/labels';
interface TopicsTreeProps { interface TopicsTreeProps {
activeTopic: HelpTopic; activeTopic: HelpTopic;
topicFolded: Map<HelpTopic, boolean>;
onChangeTopic: (newTopic: HelpTopic) => void; onChangeTopic: (newTopic: HelpTopic) => void;
onFoldTopic: (target: HelpTopic, showChildren: boolean) => void;
} }
function TopicsTree({ activeTopic, onChangeTopic }: TopicsTreeProps) { function TopicsTree({ activeTopic, topicFolded, onChangeTopic, onFoldTopic }: TopicsTreeProps) {
const handleClickFold = useCallback(
(event: CProps.EventMouse, topic: HelpTopic, showChildren: boolean) => {
event.preventDefault();
event.stopPropagation();
onFoldTopic(topic, showChildren);
},
[onFoldTopic]
);
return ( return (
<AnimatePresence initial={false}> <AnimatePresence initial={false}>
{Object.values(HelpTopic).map((topic, index) => ( {Object.values(HelpTopic).map((topic, index) => {
<div const parent = topicParent.get(topic);
key={`${prefixes.topic_list}${index}`} if (parent !== topic && topicFolded.get(topicParent.get(topic)!)) {
className={clsx( return null;
'px-3 py-1 cc-scroll-row', }
'clr-controls clr-hover', const isFoldable = !!foldableTopics.find(id => id === topic);
'cursor-pointer', const isFolded = topicFolded.get(topic)!;
activeTopic === topic && 'clr-selected' return (
)} <motion.div
title={describeHelpTopic(topic)} tabIndex={-1}
onClick={() => onChangeTopic(topic)} key={`${prefixes.topic_list}${index}`}
> className={clsx(
{labelHelpTopic(topic)} 'pr-3 pl-6 py-1',
</div> 'cc-scroll-row',
))} 'clr-controls clr-hover',
'cursor-pointer',
activeTopic === topic && 'clr-selected'
)}
title={describeHelpTopic(topic)}
onClick={() => onChangeTopic(topic)}
initial={{ ...animateSideAppear.initial }}
animate={{ ...animateSideAppear.animate }}
exit={{ ...animateSideAppear.exit }}
>
{isFoldable ? (
<Overlay position='left-[-1.3rem]' className={clsx(!isFolded && 'top-[0.1rem]')}>
<MiniButton
tabIndex={-1}
noPadding
noHover
icon={!isFolded ? <IconDropArrow size='1rem' /> : <IconPageRight size='1.25rem' />}
onClick={event => handleClickFold(event, topic, isFolded)}
/>
</Overlay>
) : null}
{labelHelpTopic(topic)}
</motion.div>
);
})}
</AnimatePresence> </AnimatePresence>
); );
} }

View File

@ -74,7 +74,7 @@ function ConstituentaToolbar({
disabled={disabled || modified} disabled={disabled || modified}
onClick={onMoveDown} onClick={onMoveDown}
/> />
<BadgeHelp topic={HelpTopic.RSFORM_EDITOR} offset={4} /> <BadgeHelp topic={HelpTopic.UI_RSFORM_EDITOR} offset={4} />
</Overlay> </Overlay>
); );
} }

View File

@ -205,7 +205,7 @@ function EditorRSExpression({
parseData={parser.parseData} parseData={parser.parseData}
onAnalyze={() => handleCheckExpression()} onAnalyze={() => handleCheckExpression()}
/> />
<BadgeHelp topic={HelpTopic.CST_STATUS} offset={4} /> <BadgeHelp topic={HelpTopic.UI_CST_STATUS} offset={4} />
</Overlay> </Overlay>
<RSInput <RSInput

View File

@ -81,7 +81,7 @@ function RSFormToolbar({ modified, anonymous, subscribed, claimable, onSubmit, o
onClick={onDestroy} onClick={onDestroy}
/> />
) : null} ) : null}
<BadgeHelp topic={HelpTopic.RSFORM_CARD} offset={4} className='max-w-[32rem]' /> <BadgeHelp topic={HelpTopic.UI_RSFORM_CARD} offset={4} className='max-w-[32rem]' />
</Overlay> </Overlay>
); );
} }

View File

@ -68,7 +68,7 @@ function RSListToolbar() {
disabled={controller.isProcessing || controller.nothingSelected} disabled={controller.isProcessing || controller.nothingSelected}
onClick={controller.deleteCst} onClick={controller.deleteCst}
/> />
<BadgeHelp topic={HelpTopic.RSFORM_LIST} offset={5} /> <BadgeHelp topic={HelpTopic.UI_RSFORM_LIST} offset={5} />
</Overlay> </Overlay>
); );
} }

View File

@ -28,8 +28,8 @@ function GraphSelectors({ coloring, setColoring, layout, setLayout, sizing, setS
onChange={data => setLayout(data?.value ?? SelectorGraphLayout[0].value)} onChange={data => setLayout(data?.value ?? SelectorGraphLayout[0].value)}
/> />
<Overlay position='right-[2.5rem] top-[0.5rem]'> <Overlay position='right-[2.5rem] top-[0.5rem]'>
{coloring === 'status' ? <BadgeHelp topic={HelpTopic.CST_STATUS} className='min-w-[25rem]' /> : null} {coloring === 'status' ? <BadgeHelp topic={HelpTopic.UI_CST_STATUS} className='min-w-[25rem]' /> : null}
{coloring === 'type' ? <BadgeHelp topic={HelpTopic.CST_CLASS} className='min-w-[25rem]' /> : null} {coloring === 'type' ? <BadgeHelp topic={HelpTopic.UI_CST_CLASS} className='min-w-[25rem]' /> : null}
</Overlay> </Overlay>
<SelectSingle <SelectSingle
className='my-1' className='my-1'

View File

@ -111,7 +111,7 @@ function GraphToolbar({
title='Сохранить изображение' title='Сохранить изображение'
onClick={onSaveImage} onClick={onSaveImage}
/> />
<BadgeHelp topic={HelpTopic.GRAPH_TERM} className='max-w-[calc(100vw-4rem)]' offset={4} /> <BadgeHelp topic={HelpTopic.UI_GRAPH_TERM} className='max-w-[calc(100vw-4rem)]' offset={4} />
</div> </div>
); );
} }

View File

@ -208,6 +208,28 @@ export const animateSideView = {
} }
}; };
export const animateSideAppear = {
initial: {
clipPath: 'inset(0% 100% 0% 0%)'
},
animate: {
clipPath: 'inset(0% 0% 0% 0%)',
transition: {
type: 'spring',
bounce: 0,
duration: 0.3
}
},
exit: {
clipPath: 'inset(0% 100% 0% 0%)',
transition: {
type: 'spring',
bounce: 0,
duration: 0.3
}
}
};
export const animateModal = { export const animateModal = {
initial: { initial: {
clipPath: 'inset(50% 50% 50% 50%)', clipPath: 'inset(50% 50% 50% 50%)',

View File

@ -361,14 +361,14 @@ export function labelHelpTopic(topic: HelpTopic): string {
case HelpTopic.MAIN: return 'Портал'; case HelpTopic.MAIN: return 'Портал';
case HelpTopic.INTERFACE: return 'Интерфейс'; case HelpTopic.INTERFACE: return 'Интерфейс';
case HelpTopic.LIBRARY: return '- библиотека'; case HelpTopic.UI_LIBRARY: return '- библиотека';
case HelpTopic.RSFORM_UI: return '- концептуальная схема'; case HelpTopic.UI_RSFORM: return '- концептуальная схема';
case HelpTopic.RSFORM_CARD: return '= карточка схемы'; case HelpTopic.UI_RSFORM_CARD: return '= карточка схемы';
case HelpTopic.RSFORM_LIST: return '= список конституент'; case HelpTopic.UI_RSFORM_LIST: return '= список конституент';
case HelpTopic.RSFORM_EDITOR: return '= редактор конституенты'; case HelpTopic.UI_RSFORM_EDITOR: return '= редактор конституенты';
case HelpTopic.GRAPH_TERM: return '= граф термов'; case HelpTopic.UI_GRAPH_TERM: return '= граф термов';
case HelpTopic.CST_STATUS: return '= статус конституенты'; case HelpTopic.UI_CST_STATUS: return '= статус конституенты';
case HelpTopic.CST_CLASS: return '= класс конституенты'; case HelpTopic.UI_CST_CLASS: return '= класс конституенты';
case HelpTopic.CONCEPTUAL: return 'Концептуализация'; case HelpTopic.CONCEPTUAL: return 'Концептуализация';
case HelpTopic.CC_SYSTEM: return '- система определений'; case HelpTopic.CC_SYSTEM: return '- система определений';
@ -399,14 +399,14 @@ export function describeHelpTopic(topic: HelpTopic): string {
case HelpTopic.MAIN: return 'Общая справка по порталу'; case HelpTopic.MAIN: return 'Общая справка по порталу';
case HelpTopic.INTERFACE: return 'Описание интерфейса пользователя'; case HelpTopic.INTERFACE: return 'Описание интерфейса пользователя';
case HelpTopic.LIBRARY: return 'Интерфейс Библиотеки схем'; case HelpTopic.UI_LIBRARY: return 'Интерфейс Библиотеки схем';
case HelpTopic.RSFORM_UI: return 'Просмотр и редактирование концептуальной схемы'; case HelpTopic.UI_RSFORM: return 'Просмотр и редактирование концептуальной схемы';
case HelpTopic.RSFORM_CARD: return 'Интерфейс Карточки схемы'; case HelpTopic.UI_RSFORM_CARD: return 'Интерфейс Карточки схемы';
case HelpTopic.RSFORM_LIST: return 'Интерфейс Списка конституент'; case HelpTopic.UI_RSFORM_LIST: return 'Интерфейс Списка конституент';
case HelpTopic.RSFORM_EDITOR: return 'Интерфейс редактирования конституенты'; case HelpTopic.UI_RSFORM_EDITOR: return 'Интерфейс редактирования конституенты';
case HelpTopic.GRAPH_TERM: return 'Интерфейс графа термов'; case HelpTopic.UI_GRAPH_TERM: return 'Интерфейс графа термов';
case HelpTopic.CST_STATUS: return 'Нотация отображения статуса конституенты'; case HelpTopic.UI_CST_STATUS: return 'Нотация отображения статуса конституенты';
case HelpTopic.CST_CLASS: return 'Нотация отображения класса конституенты'; case HelpTopic.UI_CST_CLASS: return 'Нотация отображения класса конституенты';
case HelpTopic.CONCEPTUAL: return 'Основы концептуализации и концептуального мышления'; case HelpTopic.CONCEPTUAL: return 'Основы концептуализации и концептуального мышления';
case HelpTopic.CC_SYSTEM: return 'Концептуальная схема как система понятий'; case HelpTopic.CC_SYSTEM: return 'Концептуальная схема как система понятий';