Refactoring constants + small UI fixes

This commit is contained in:
IRBorisov 2024-04-06 14:39:49 +03:00
parent 16252b2145
commit a10bda8af3
15 changed files with 51 additions and 72 deletions

View File

@ -4,6 +4,7 @@ import { motion } from 'framer-motion';
import { IconPin, IconUnpin } from '@/components/Icons';
import { useConceptOptions } from '@/context/OptionsContext';
import { animateNavigationToggle } from '@/styling/animations';
import { globals } from '@/utils/constants';
function ToggleNavigationButton() {
const { noNavigationAnimation, toggleNoNavigation } = useConceptOptions();
@ -11,7 +12,6 @@ function ToggleNavigationButton() {
<motion.button
type='button'
tabIndex={-1}
title={noNavigationAnimation ? 'Показать навигацию' : 'Скрыть навигацию'}
className={clsx(
'absolute top-0 right-0 z-navigation flex items-center justify-center',
'clr-btn-nav',
@ -21,6 +21,8 @@ function ToggleNavigationButton() {
initial={false}
animate={noNavigationAnimation ? 'off' : 'on'}
variants={animateNavigationToggle}
data-tooltip-id={globals.tooltip}
data-tooltip-content={noNavigationAnimation ? 'Показать навигацию' : 'Скрыть навигацию'}
>
{!noNavigationAnimation ? <IconPin /> : null}
{noNavigationAnimation ? <IconUnpin /> : null}

View File

@ -22,7 +22,8 @@ export { LuGlasses as IconReader } from 'react-icons/lu';
export { FiBell as IconFollow } from 'react-icons/fi';
export { FiBellOff as IconFollowOff } from 'react-icons/fi';
export { BiListUl as IconList } from 'react-icons/bi';
export { TbColumns as IconList } from 'react-icons/tb';
export { TbColumnsOff as IconListOff } from 'react-icons/tb';
export { BiFontFamily as IconText } from 'react-icons/bi';
export { BiFont as IconTextOff } from 'react-icons/bi';
export { RiTreeLine as IconTree } from 'react-icons/ri';

View File

@ -4,8 +4,6 @@ import EmbedYoutube from '@/components/ui/EmbedYoutube';
import useWindowSize from '@/hooks/useWindowSize';
import { external_urls, youtube } from '@/utils/constants';
const OPT_VIDEO_H = 1080;
function HelpRSLang() {
const windowSize = useWindowSize();
@ -13,7 +11,7 @@ function HelpRSLang() {
const viewH = windowSize.height ?? 0;
const viewW = windowSize.width ?? 0;
const availableWidth = viewW - (windowSize.isSmall ? 35 : 290);
return Math.min(OPT_VIDEO_H, viewH - 320, Math.floor((availableWidth * 9) / 16));
return Math.min(1080, viewH - 320, Math.floor((availableWidth * 9) / 16));
}, [windowSize]);
// prettier-ignore

View File

@ -8,8 +8,7 @@ import { getDefinitionPrefix } from '@/models/rsformAPI';
import { IArgumentInfo, IExpressionParse } from '@/models/rslang';
import { RSErrorType } from '@/models/rslang';
import { DataCallback, postCheckExpression } from '@/utils/backendAPI';
const LOGIC_TYPIFICATION = 'LOGIC';
import { PARAMETER } from '@/utils/constants';
function useCheckExpression({ schema }: { schema?: IRSForm }) {
const [loading, setLoading] = useState(false);
@ -47,17 +46,17 @@ function checkTypeConsistency(type: CstType, typification: string, args: IArgume
case CstType.CONSTANT:
case CstType.STRUCTURED:
case CstType.TERM:
return typification !== LOGIC_TYPIFICATION && args.length === 0;
return typification !== PARAMETER.logicLabel && args.length === 0;
case CstType.AXIOM:
case CstType.THEOREM:
return typification === LOGIC_TYPIFICATION && args.length === 0;
return typification === PARAMETER.logicLabel && args.length === 0;
case CstType.FUNCTION:
return typification !== LOGIC_TYPIFICATION && args.length !== 0;
return typification !== PARAMETER.logicLabel && args.length !== 0;
case CstType.PREDICATE:
return typification === LOGIC_TYPIFICATION && args.length !== 0;
return typification === PARAMETER.logicLabel && args.length !== 0;
}
}

View File

@ -2,13 +2,10 @@
import { useCallback, useEffect } from 'react';
const KEY_NAME_ESC = 'Escape';
const KEY_EVENT_TYPE = 'keyup';
function useEscapeKey(handleClose: () => void) {
const handleEscKey = useCallback(
(event: KeyboardEvent) => {
if (event.key === KEY_NAME_ESC) {
if (event.key === 'Escape') {
handleClose();
}
},
@ -16,9 +13,9 @@ function useEscapeKey(handleClose: () => void) {
);
useEffect(() => {
document.addEventListener(KEY_EVENT_TYPE, handleEscKey, false);
document.addEventListener('keyup', handleEscKey, false);
return () => {
document.removeEventListener(KEY_EVENT_TYPE, handleEscKey, false);
document.removeEventListener('keyup', handleEscKey, false);
};
}, [handleEscKey]);
}

View File

@ -2,7 +2,7 @@
import { useEffect, useState } from 'react';
import { SMALL_SCREEN_WIDTH } from '@/utils/constants';
import { PARAMETER } from '@/utils/constants';
function useWindowSize() {
const isClient = typeof window === 'object';
@ -11,7 +11,7 @@ function useWindowSize() {
return {
width: isClient ? window.innerWidth : undefined,
height: isClient ? window.innerHeight : undefined,
isSmall: isClient && window.innerWidth < SMALL_SCREEN_WIDTH
isSmall: isClient && window.innerWidth < PARAMETER.smallScreen
};
}

View File

@ -32,9 +32,7 @@ export function loadRSFormData(input: IRSFormData): IRSForm {
result.graph = new Graph();
result.stats = calculateStats(result.items);
const derivationLookup: Map<ConstituentaID, ConstituentaID> = new Map();
result.items.forEach(cst => {
derivationLookup.set(cst.id, cst.id);
cst.derived_from = cst.id;
cst.derived_children = [];
cst.derived_children_alias = [];
@ -51,7 +49,9 @@ export function loadRSFormData(input: IRSFormData): IRSForm {
});
});
// Calculate derivation of constituents based on formal definition analysis
const derivationLookup: Map<ConstituentaID, ConstituentaID> = new Map();
result.graph.topologicalOrder().forEach(id => {
derivationLookup.set(id, id);
const cst = result.items.find(item => item.id === id)!;
if (cst.cst_type === CstType.STRUCTURED) {
return;

View File

@ -3,7 +3,7 @@ import { useLayoutEffect } from 'react';
import { urls } from '@/app/urls';
import { useAuth } from '@/context/AuthContext';
import { useConceptNavigation } from '@/context/NavigationContext';
import { TIMEOUT_UI_REFRESH } from '@/utils/constants';
import { PARAMETER } from '@/utils/constants';
function HomePage() {
const router = useConceptNavigation();
@ -13,11 +13,11 @@ function HomePage() {
if (!user) {
setTimeout(() => {
router.push(urls.manuals);
}, TIMEOUT_UI_REFRESH);
}, PARAMETER.refreshTimeout);
} else {
setTimeout(() => {
router.push(urls.library);
}, TIMEOUT_UI_REFRESH);
}, PARAMETER.refreshTimeout);
}
}, [router, user]);

View File

@ -14,9 +14,6 @@ import ViewConstituents from '../ViewConstituents';
import ConstituentaToolbar from './ConstituentaToolbar';
import FormConstituenta from './FormConstituenta';
// Max height of content for left editor pane.
const UNFOLDED_HEIGHT = '59.1rem';
// Threshold window width to switch layout.
const SIDELIST_LAYOUT_THRESHOLD = 1100; // px
@ -119,7 +116,6 @@ function EditorConstituenta({ activeCst, isModified, setIsModified, onOpenEdit }
schema={controller.schema}
expression={activeCst?.definition_formal ?? ''}
isBottom={isNarrow}
baseHeight={UNFOLDED_HEIGHT}
activeID={activeCst?.id}
onOpenEdit={onOpenEdit}
/>

View File

@ -6,7 +6,7 @@ import { useCallback, useLayoutEffect, useRef, useState } from 'react';
import { FaRegKeyboard } from 'react-icons/fa6';
import { toast } from 'react-toastify';
import { IconList, IconText, IconTree } from '@/components/Icons';
import { IconList, IconListOff, IconText, IconTextOff, IconTree } from '@/components/Icons';
import BadgeHelp from '@/components/man/BadgeHelp';
import RSInput from '@/components/RSInput';
import { RSTextWrapper } from '@/components/RSInput/textEditing';
@ -164,19 +164,21 @@ function EditorRSExpression({
<MiniButton
title='Изменить шрифт'
onClick={toggleFont}
icon={<IconText size='1.25rem' className={mathFont === 'math' ? 'icon-primary' : ''} />}
icon={
mathFont === 'math' ? <IconText size='1.25rem' className='icon-primary' /> : <IconTextOff size='1.25rem' />
}
/>
{!disabled || model.processing ? (
<MiniButton
title='Отображение специальной клавиатуры'
onClick={() => setShowControls(prev => !prev)}
icon={<FaRegKeyboard size='1.25rem' className={showControls ? 'icon-primary' : ''} />}
onClick={() => setShowControls(prev => !prev)}
/>
) : null}
<MiniButton
title='Отображение списка конституент'
icon={showList ? <IconList size='1.25rem' className='icon-primary' /> : <IconListOff size='1.25rem' />}
onClick={onToggleList}
icon={<IconList size='1.25rem' className={showList ? 'icon-primary' : ''} />}
/>
<MiniButton
title='Дерево разбора выражения'

View File

@ -14,7 +14,7 @@ import useLocalStorage from '@/hooks/useLocalStorage';
import { GraphColoringScheme, GraphFilterParams } from '@/models/miscellaneous';
import { ConstituentaID, CstType } from '@/models/rsform';
import { colorBgGraphNode } from '@/styling/color';
import { storage, TIMEOUT_GRAPH_REFRESH } from '@/utils/constants';
import { PARAMETER, storage } from '@/utils/constants';
import { useRSEdit } from '../RSEditContext';
import GraphSelectors from './GraphSelectors';
@ -141,7 +141,7 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) {
setLayout(newLayout);
setTimeout(() => {
setToggleResetView(prev => !prev);
}, TIMEOUT_GRAPH_REFRESH);
}, PARAMETER.graphRefreshDelay);
}
const handleChangeParams = useCallback(
@ -173,7 +173,7 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) {
}));
setTimeout(() => {
setToggleResetView(prev => !prev);
}, TIMEOUT_GRAPH_REFRESH);
}, PARAMETER.graphRefreshDelay);
}, [setFilterParams, setToggleResetView]);
const graph = useMemo(

View File

@ -6,7 +6,7 @@ import GraphUI, { GraphCanvasRef, GraphEdge, GraphNode, LayoutTypes, useSelectio
import { useConceptOptions } from '@/context/OptionsContext';
import { ConstituentaID } from '@/models/rsform';
import { graphDarkT, graphLightT } from '@/styling/color';
import { resources } from '@/utils/constants';
import { PARAMETER, resources } from '@/utils/constants';
interface TermGraphProps {
nodes: GraphNode[];
@ -25,8 +25,6 @@ interface TermGraphProps {
toggleResetView: boolean;
}
const TREE_SIZE_MILESTONE = 50;
function TermGraph({
nodes,
edges,
@ -114,7 +112,7 @@ function TermGraph({
maxNodeSize={8}
cameraMode={orbit ? 'orbit' : is3D ? 'rotate' : 'pan'}
layoutOverrides={
layout.includes('tree') ? { nodeLevelRatio: nodes.length < TREE_SIZE_MILESTONE ? 3 : 1 } : undefined
layout.includes('tree') ? { nodeLevelRatio: nodes.length < PARAMETER.smallTreeNodes ? 3 : 1 } : undefined
}
labelFontUrl={resources.graph_font}
theme={darkMode ? graphDarkT : graphLightT}

View File

@ -14,7 +14,7 @@ import { useConceptOptions } from '@/context/OptionsContext';
import { useRSForm } from '@/context/RSFormContext';
import useQueryStrings from '@/hooks/useQueryStrings';
import { ConstituentaID, IConstituenta, IConstituentaMeta } from '@/models/rsform';
import { prefixes, TIMEOUT_UI_REFRESH } from '@/utils/constants';
import { PARAMETER, prefixes } from '@/utils/constants';
import { labelVersion } from '@/utils/labels';
import EditorConstituenta from './EditorConstituenta';
@ -124,7 +124,7 @@ function RSTabs() {
inline: 'nearest'
});
}
}, TIMEOUT_UI_REFRESH);
}, PARAMETER.refreshTimeout);
}
},
[activeTab, navigateTab]

View File

@ -2,7 +2,7 @@
import clsx from 'clsx';
import { motion } from 'framer-motion';
import { useMemo, useState } from 'react';
import { useState } from 'react';
import { useConceptOptions } from '@/context/OptionsContext';
import { ConstituentaID, IConstituenta, IRSForm } from '@/models/rsform';
@ -11,33 +11,22 @@ import { animateSideView } from '@/styling/animations';
import ConstituentsSearch from './ConstituentsSearch';
import ConstituentsTable from './ConstituentsTable';
// Height that should be left to accommodate navigation panel + bottom margin
const LOCAL_NAVIGATION_H = '2.1rem';
// Window width cutoff for expression show
const COLUMN_EXPRESSION_HIDE_THRESHOLD = 1500;
interface ViewConstituentsProps {
expression: string;
isBottom?: boolean;
baseHeight: string;
activeID?: ConstituentaID;
schema?: IRSForm;
onOpenEdit: (cstID: ConstituentaID) => void;
}
function ViewConstituents({ expression, schema, activeID, isBottom, baseHeight, onOpenEdit }: ViewConstituentsProps) {
const { noNavigation } = useConceptOptions();
function ViewConstituents({ expression, schema, activeID, isBottom, onOpenEdit }: ViewConstituentsProps) {
const { calculateHeight } = useConceptOptions();
const [filteredData, setFilteredData] = useState<IConstituenta[]>(schema?.items ?? []);
const maxHeight = useMemo(() => {
const siblingHeight = `${baseHeight} - ${LOCAL_NAVIGATION_H}`;
return noNavigation
? `calc(min(100vh - 8.2rem, ${siblingHeight}))`
: `calc(min(100vh - 11.7rem, ${siblingHeight}))`;
}, [noNavigation, baseHeight]);
return (
<motion.div
className={clsx(
@ -58,7 +47,7 @@ function ViewConstituents({ expression, schema, activeID, isBottom, baseHeight,
setFiltered={setFilteredData}
/>
<ConstituentsTable
maxHeight={isBottom ? '12rem' : maxHeight}
maxHeight={isBottom ? '12rem' : calculateHeight('8.2rem')}
items={filteredData}
activeID={activeID}
onOpenEdit={onOpenEdit}

View File

@ -10,24 +10,16 @@ export const buildConstants = {
};
/**
* General UI timeout [in ms] for waiting for render.
* Global application Parameters. The place where magic numbers are put to rest.
*/
export const TIMEOUT_UI_REFRESH = 100;
export const PARAMETER = {
smallScreen: 640, // == tailwind:xs
smallTreeNodes: 50, // amount of nodes threshold for size increase for large graphs
refreshTimeout: 100, // milliseconds delay for post-refresh actions
graphRefreshDelay: 10, // milliseconds delay for graph viewpoint reset
/**
* Threshold for small screen size optimizations.
*/
export const SMALL_SCREEN_WIDTH = 640; // == tailwind:xs
/**
* Timeout [in ms] for graph refresh.
*/
export const TIMEOUT_GRAPH_REFRESH = 200;
/**
* Exteor file extension for RSForm.
*/
export const EXTEOR_TRS_FILE = '.trs';
logicLabel: 'LOGIC'
};
/**
* Numeric limitations.
@ -36,6 +28,11 @@ export const limits = {
library_alias_len: 12
};
/**
* Exteor file extension for RSForm.
*/
export const EXTEOR_TRS_FILE = '.trs';
/**
* Regex patterns for data validation.
*/