UI improvements for small screens

This commit is contained in:
IRBorisov 2024-02-22 15:07:05 +03:00
parent 48d9eea153
commit ba658d4d0e
14 changed files with 213 additions and 74 deletions

View File

@ -4,24 +4,24 @@ import { urls } from '@/utils/constants';
function HelpExteor() {
// prettier-ignore
return (
<div className='flex flex-col gap-2'>
<div>
<h1>Экстеор</h1>
<p>Экстеор 4.9 редактор текстов систем понятий эксплицированных в родах структур, используемых для создания проектов систем организационного управления. Экстеор является идейным предком Портала.</p>
<p>Портал превосходит Экстеор в части редактирования экспликаций, но функции синтеза и вычисления интерпретации пока доступны только в Экстеоре. Также следует использовать Экстеор для выгрузки экспликаций в Word для последующей печати.</p>
<p>Экстеор доступен на операционной системы Windows 10 и выше.</p>
<p>Экстеор 4.9 редактор текстов систем понятий эксплицированных в родах структур</p>
<p>Портал превосходит Экстеор в части редактирования экспликаций, но функции синтеза и вычисления интерпретации пока доступны только в Экстеоре. Также следует использовать Экстеор для выгрузки экспликаций в Word для последующей печати</p>
<p>Экстеор доступен на операционной системы Windows 10+</p>
<p>Скачать установщик: <TextURL href={urls.exteor64} text='64bit'/> | <TextURL href={urls.exteor32} text='32bit'/></p>
<p className='mt-2'><b>Основные функции</b></p>
<li>Работа с РС-формой системы понятий.</li>
<li>Автоматическое определение типизации выражений языка родов структур.</li>
<li>Проверка корректности РС-формы.</li>
<li>Контекстный поиск с учетом словоформ терминов.</li>
<li>Терминологический контроль вхождений терминов в тексты других терминов и текстовые определения.</li>
<li>Автоматическое выполнение операций синтеза РС-форм.</li>
<li>Синтез РС-форм с помощью операционной схемы синтеза (ОСС).</li>
<li>Автоматическое сквозное внесение изменений в ОСС (пересинтез).</li>
<li>Вычисление объектной интерпретации для производных понятий для заданных интерпретаций базовых понятий.</li>
<li>Выгрузка концептуальных схем в Word.</li>
<li>Импорт/экспорт данных объектных интерпретаций из/в Excel.</li>
<h2>Основные функции</h2>
<li>Работа с РС-формой системы понятий</li>
<li>Автоматическое определение типизации выражений</li>
<li>Проверка корректности РС-формы</li>
<li>Контекстный поиск с учетом словоформ терминов</li>
<li>Терминологический контроль вхождений терминов</li>
<li>Автоматическое выполнение операций синтеза РС-форм</li>
<li>Синтез с помощью операционной схемы синтеза (ОСС)</li>
<li>Автоматическое сквозное внесение изменений в ОСС</li>
<li>Вычисление объектной интерпретации</li>
<li>Выгрузка концептуальных схем в Word</li>
<li>Импорт/экспорт интерпретаций через Excel</li>
</div>);
}

View File

@ -6,13 +6,14 @@ function HelpLibrary() {
return (
<div>
<h1>Библиотека схем</h1>
<p>В библиотеки собраны концептуальные схемы - системы понятий, используемые в теории и практике концептуального проектирования систем организационного управления</p>
<p>В библиотеки собраны концептуальные схемы, используемые в теории и практике концептуального проектирования систем организационного управления</p>
<p>Фильтрация с помощью инструментов в верхней части страницы</p>
<p>Сортировка по клику на заголовок таблицы</p>
<h2>Отображение статусов</h2>
<div className='flex items-center gap-2'>
<FiBell size='1rem'/>
<p><b>отслеживаемая</b> обозначает отслеживание схемы</p>
p</div>
</div>
<div className='flex items-center gap-2'>
<BiShareAlt size='1rem'/>
<p><b>общедоступная</b> отображает схему всем пользователям</p>

View File

@ -6,16 +6,16 @@ function HelpMain() {
return (
<div>
<h1>Портал</h1>
<p>Портал позволяет анализировать предметные области, формально записывать системы определений и синтезировать их с помощью математического аппарата родов структур.</p>
<p className='mt-4 mb-1 text-center'><b>Основные разделы</b></p>
<p>Портал позволяет анализировать предметные области, формально записывать системы определений и синтезировать их с помощью математического аппарата родов структур</p>
<h2>Основные разделы</h2>
<li><TextURL text='Библиотека' href='/library' /> - библиотека концептуальных схем</li>
<li><TextURL text='Профиль' href='/profile' /> - данные пользователя и смена пароля</li>
<p className='mt-4 mb-1 text-center'><b>Навигация</b></p>
<h2>Навигация</h2>
<p>Навигационную панель можно скрыть с помощью кнопки в правом верхнем углу</p>
<p>В меню пользователя (правый верхний угол) доступно редактирование данных пользователя и изменение цветовой темы</p>
<p>В меню пользователя (правый угол) доступно редактирование пользователя и изменение цветовой темы</p>
<p className='mt-4 mb-1 text-center'><b>Поддержка</b></p>
<h2>Поддержка</h2>
<p>Портал разрабатывается <TextURL text='Центром Концепт' href={urls.concept}/> и является проектом с открытым исходным кодом, доступным на <TextURL text='Github' href={urls.git_repo}/></p>
<p>Ваши пожелания по доработке, найденные ошибки и иные предложения можно направлять по email: <TextURL href={urls.mail_portal} text='portal@acconcept.ru'/></p>
</div>);

View File

@ -2,7 +2,11 @@ import PDFViewer from '@/components/PDFViewer';
import { resources } from '@/utils/constants';
function HelpPrivacy() {
return <PDFViewer file={resources.privacy_policy} />;
return (
<div>
<PDFViewer file={resources.privacy_policy} />
</div>
);
}
export default HelpPrivacy;

View File

@ -12,7 +12,8 @@ function HelpRSLang() {
const videoHeight = useMemo(() => {
const viewH = windowSize.height ?? 0;
const viewW = windowSize.width ?? 0;
return Math.min(OPT_VIDEO_H, viewH - 320, Math.floor(((viewW - 290) * 9) / 16));
const availableWidth = viewW - (windowSize.isSmall ? 35 : 290);
return Math.min(OPT_VIDEO_H, viewH - 320, Math.floor((availableWidth * 9) / 16));
}, [windowSize]);
// prettier-ignore

View File

@ -1,16 +1,16 @@
function HelpRSTemplates() {
// prettier-ignore
return (
<div className='flex flex-col gap-2 pb-2'>
<div>
<h1>Банк выражений</h1>
<p>Портал предоставляет быстрый доступ к часто используемым выражениям с помощью функции создания конституенты из шаблона.</p>
<p>Источником шаблонов является <b>Банк выражений</b>, содержащий параметризованные понятия и утверждения, сгруппированные по разделам.</p>
<p>Сначала выбирается шаблон выражения (вкладка Шаблон).</p>
<p>Далее для аргументов можно зафиксировать значения, выбрав из конституент текущей схемы или указав выражения (вкладка Аргументы).</p>
<p>Значения аргументов будут подставлены в выражение, включая корректировку перечня аргументов.</p>
<p>Если значения указаны для всех аргументов, то тип создаваемой конституенты будет автоматически обновлён.</p>
<p>На вкладке Конституента можно скорректировать все атрибуты, создаваемой конституенты.</p>
<p>Кнопка <b>Создать</b> инициирует добавление выбранной конституенты в схему.</p>
<p>Портал предоставляет быстрый доступ к часто используемым выражениям с помощью функции создания конституенты из шаблона</p>
<p>Источником шаблонов является <b>Банк выражений</b>, содержащий параметризованные понятия и утверждения, сгруппированные по разделам</p>
<p>Сначала выбирается шаблон выражения (вкладка Шаблон)</p>
<p>Далее для аргументов можно зафиксировать значения, выбрав из конституент текущей схемы или указав выражения (вкладка Аргументы)</p>
<p>Значения аргументов будут подставлены в выражение, включая корректировку перечня аргументов</p>
<p>Если значения указаны для всех аргументов, то тип создаваемой конституенты будет автоматически обновлён</p>
<p>На вкладке Конституента можно скорректировать все атрибуты, создаваемой конституенты</p>
<p>Кнопка <b>Создать</b> инициирует добавление выбранной конституенты в схему</p>
</div>);
}

View File

@ -11,7 +11,7 @@ import Overlay from '../ui/Overlay';
import PageControls from './PageControls';
const MAXIMUM_WIDTH = 1000;
const MINIMUM_WIDTH = 600;
const MINIMUM_WIDTH = 320;
interface PDFViewerProps {
file?: string | ArrayBuffer | Blob;
@ -24,7 +24,7 @@ function PDFViewer({ file }: PDFViewerProps) {
const [pageNumber, setPageNumber] = useState(1);
const pageWidth = useMemo(() => {
return Math.max(MINIMUM_WIDTH, Math.min((windowSize?.width ?? 0) - 300, MAXIMUM_WIDTH));
return Math.max(MINIMUM_WIDTH, Math.min((windowSize?.width ?? 0) - 10, MAXIMUM_WIDTH));
}, [windowSize]);
function onDocumentLoadSuccess({ numPages }: PDFDocumentProxy) {
@ -35,15 +35,14 @@ function PDFViewer({ file }: PDFViewerProps) {
<Document
file={file}
onLoadSuccess={onDocumentLoadSuccess}
className='px-3'
loading='Загрузка PDF файла...'
error='Не удалось загрузить файл.'
>
<Overlay position='top-6 left-1/2 -translate-x-1/2' className='flex select-none'>
<Overlay position='top-3 left-1/2 -translate-x-1/2' className='flex select-none'>
<PageControls pageCount={pageCount} pageNumber={pageNumber} setPageNumber={setPageNumber} />
</Overlay>
<Page
className='pointer-events-none select-none'
className='pointer-events-none select-none sm:translate-x-0'
renderTextLayer={false}
renderAnnotationLayer={false}
pageNumber={pageNumber}

View File

@ -25,7 +25,7 @@ function PageControls({ pageNumber, pageCount, setPageNumber }: PageControlsProp
>
<BiChevronLeft size='1.5rem' />
</button>
<p className='px-3 text-black'>
<p className='px-3 text-black text-nowrap'>
Страница {pageNumber} из {pageCount}
</p>
<button

View File

@ -1,8 +1,10 @@
import clsx from 'clsx';
'use client';
import useWindowSize from '@/hooks/useWindowSize';
import { HelpTopic } from '@/models/miscellaneous';
import { prefixes } from '@/utils/constants';
import { describeHelpTopic, labelHelpTopic } from '@/utils/labels';
import TopicsListDropDown from './TopicsListDropdown';
import TopicsListStatic from './TopicsListStatic';
interface TopicsListProps {
activeTopic: HelpTopic;
@ -10,35 +12,13 @@ interface TopicsListProps {
}
function TopicsList({ activeTopic, onChangeTopic }: TopicsListProps) {
return (
<div
className={clsx(
'sticky top-0 left-0',
'self-start',
'border-x',
'clr-controls',
'text-xs sm:text-sm',
'select-none'
)}
>
{Object.values(HelpTopic).map((topic, index) => (
<div
key={`${prefixes.topic_list}${index}`}
className={clsx(
'px-3 py-1',
'border-y',
'clr-hover',
'cursor-pointer',
activeTopic === topic && 'clr-selected'
)}
title={describeHelpTopic(topic)}
onClick={() => onChangeTopic(topic)}
>
{labelHelpTopic(topic)}
</div>
))}
</div>
);
const size = useWindowSize();
if (!size.isSmall) {
return <TopicsListStatic activeTopic={activeTopic} onChangeTopic={onChangeTopic} />;
} else {
return <TopicsListDropDown activeTopic={activeTopic} onChangeTopic={onChangeTopic} />;
}
}
export default TopicsList;

View File

@ -0,0 +1,84 @@
'use client';
import clsx from 'clsx';
import { motion } from 'framer-motion';
import { useCallback } from 'react';
import { RiMenuFoldFill, RiMenuUnfoldFill } from 'react-icons/ri';
import Button from '@/components/ui/Button';
import { useConceptTheme } from '@/context/ThemeContext';
import useDropdown from '@/hooks/useDropdown';
import { HelpTopic } from '@/models/miscellaneous';
import { animateSlideLeft } from '@/styling/animations';
import { prefixes } from '@/utils/constants';
import { describeHelpTopic, labelHelpTopic } from '@/utils/labels';
interface TopicsListDropDownProps {
activeTopic: HelpTopic;
onChangeTopic: (newTopic: HelpTopic) => void;
}
function TopicsListDropDown({ activeTopic, onChangeTopic }: TopicsListDropDownProps) {
const menu = useDropdown();
const { noNavigation } = useConceptTheme();
const selectTheme = useCallback(
(topic: HelpTopic) => {
menu.hide();
onChangeTopic(topic);
},
[onChangeTopic, menu]
);
return (
<div
ref={menu.ref}
className={clsx(
'absolute left-0', // prettier: split-lines
'flex flex-col',
'z-modal-tooltip',
'text-xs sm:text-sm',
'select-none',
{
'top-0': noNavigation,
'top-[3rem]': !noNavigation
}
)}
>
<Button
noOutline
tabIndex={-1}
title='Список тем'
hideTitle={menu.isOpen}
icon={!menu.isOpen ? <RiMenuUnfoldFill size='1.25rem' /> : <RiMenuFoldFill size='1.25rem' />}
className='w-[3rem] h-7'
onClick={menu.toggle}
/>
<motion.div
className='border-x'
initial={false}
animate={menu.isOpen ? 'open' : 'closed'}
variants={animateSlideLeft}
>
{Object.values(HelpTopic).map((topic, index) => (
<div
key={`${prefixes.topic_list}${index}`}
className={clsx(
'px-3 py-1',
'border-y',
'clr-controls clr-hover',
'cursor-pointer',
activeTopic === topic && 'clr-selected'
)}
title={describeHelpTopic(topic)}
onClick={() => selectTheme(topic)}
>
{labelHelpTopic(topic)}
</div>
))}
</motion.div>
</div>
);
}
export default TopicsListDropDown;

View File

@ -0,0 +1,44 @@
import clsx from 'clsx';
import { HelpTopic } from '@/models/miscellaneous';
import { prefixes } from '@/utils/constants';
import { describeHelpTopic, labelHelpTopic } from '@/utils/labels';
interface TopicsListStaticProps {
activeTopic: HelpTopic;
onChangeTopic: (newTopic: HelpTopic) => void;
}
function TopicsListStatic({ activeTopic, onChangeTopic }: TopicsListStaticProps) {
return (
<div
className={clsx(
'sticky top-0 left-0',
'self-start',
'border-x',
'clr-controls',
'text-xs sm:text-sm',
'select-none'
)}
>
{Object.values(HelpTopic).map((topic, index) => (
<div
key={`${prefixes.topic_list}${index}`}
className={clsx(
'px-3 py-1',
'border-y',
'clr-hover',
'cursor-pointer',
activeTopic === topic && 'clr-selected'
)}
title={describeHelpTopic(topic)}
onClick={() => onChangeTopic(topic)}
>
{labelHelpTopic(topic)}
</div>
))}
</div>
);
}
export default TopicsListStatic;

View File

@ -8,7 +8,7 @@ interface ViewTopicProps {
function ViewTopic({ topic }: ViewTopicProps) {
return (
<AnimateFade key={topic} className='px-2 py-2 mx-auto'>
<AnimateFade key={topic} className='py-2 pl-6 pr-3 mx-auto'>
<InfoTopic topic={topic} />
</AnimateFade>
);

View File

@ -53,6 +53,27 @@ export const animateNavigationToggle: Variants = {
}
};
export const animateSlideLeft: Variants = {
open: {
clipPath: 'inset(0% 0% 0% 0%)',
transition: {
type: 'spring',
bounce: 0,
duration: 0.4,
delayChildren: 0.2,
staggerChildren: 0.05
}
},
closed: {
clipPath: 'inset(0% 100% 0% 0%)',
transition: {
type: 'spring',
bounce: 0,
duration: 0.3
}
}
};
export const animateDropdown: Variants = {
open: {
clipPath: 'inset(0% 0% 0% 0%)',

View File

@ -112,13 +112,18 @@ li {
}
h2 {
@apply font-semibold text-center;
@apply [&:not(:first-child)]:mt-2 font-semibold text-center;
}
b {
@apply font-semibold;
}
li,
p {
@apply [&:not(:last-child)]:mb-2;
}
.border {
@apply rounded;
}