M: Improve stats labels

This commit is contained in:
Ivan 2024-08-23 12:35:05 +03:00
parent c864ec947b
commit 1305560679
11 changed files with 106 additions and 72 deletions

View File

@ -69,9 +69,12 @@ export { IoLibrary as IconLibrary2 } from 'react-icons/io5';
export { BiDiamond as IconTemplates } from 'react-icons/bi'; export { BiDiamond as IconTemplates } from 'react-icons/bi';
export { TbHexagons as IconOSS } from 'react-icons/tb'; export { TbHexagons as IconOSS } from 'react-icons/tb';
export { TbHexagon as IconRSForm } from 'react-icons/tb'; export { TbHexagon as IconRSForm } from 'react-icons/tb';
export { TbTopologyRing as IconConsolidation } from 'react-icons/tb'; export { LuNewspaper as IconDefinition } from 'react-icons/lu';
export { LuDna as IconTerminology } from 'react-icons/lu';
export { FaRegHandshake as IconConvention } from 'react-icons/fa6';
export { LiaCloneSolid as IconChild } from 'react-icons/lia'; export { LiaCloneSolid as IconChild } from 'react-icons/lia';
export { RiParentLine as IconParent } from 'react-icons/ri'; export { RiParentLine as IconParent } from 'react-icons/ri';
export { TbTopologyRing as IconConsolidation } from 'react-icons/tb';
export { BiSpa as IconPredecessor } from 'react-icons/bi'; export { BiSpa as IconPredecessor } from 'react-icons/bi';
export { LuArchive as IconArchive } from 'react-icons/lu'; export { LuArchive as IconArchive } from 'react-icons/lu';
export { LuDatabase as IconDatabase } from 'react-icons/lu'; export { LuDatabase as IconDatabase } from 'react-icons/lu';

View File

@ -1,35 +1,47 @@
import clsx from 'clsx'; import clsx from 'clsx';
import { useMemo } from 'react';
import { globals } from '@/utils/constants'; import { globals } from '@/utils/constants';
import { CProps } from '../props'; import { CProps } from '../props';
import MiniButton from './MiniButton'; import MiniButton from './MiniButton';
interface IconValueProps extends CProps.Styling, CProps.Titled { interface ValueIconProps extends CProps.Styling, CProps.Titled {
id?: string; id?: string;
icon: React.ReactNode; icon: React.ReactNode;
value: string | number; value: string | number;
textClassName?: string;
onClick?: (event: CProps.EventMouse) => void; onClick?: (event: CProps.EventMouse) => void;
smallThreshold?: number;
dense?: boolean; dense?: boolean;
disabled?: boolean; disabled?: boolean;
} }
function IconValue({ function ValueIcon({
id, id,
dense, dense,
value,
icon, icon,
value,
textClassName,
disabled = true, disabled = true,
title, title,
titleHtml, titleHtml,
hideTitle, hideTitle,
className, className,
smallThreshold,
onClick, onClick,
...restProps ...restProps
}: IconValueProps) { }: ValueIconProps) {
const isSmall = useMemo(() => !smallThreshold || String(value).length < smallThreshold, [value, smallThreshold]);
return ( return (
<div <div
className={clsx('flex items-center text-right', { 'justify-between gap-6': !dense, 'gap-1': dense }, className)} className={clsx(
'flex items-center',
'text-right',
'hover:cursor-default',
{ 'justify-between gap-6': !dense, 'gap-1': dense },
className
)}
{...restProps} {...restProps}
data-tooltip-id={!!title || !!titleHtml ? globals.tooltip : undefined} data-tooltip-id={!!title || !!titleHtml ? globals.tooltip : undefined}
data-tooltip-html={titleHtml} data-tooltip-html={titleHtml}
@ -37,11 +49,11 @@ function IconValue({
data-tooltip-hidden={hideTitle} data-tooltip-hidden={hideTitle}
> >
<MiniButton noHover noPadding icon={icon} disabled={disabled} onClick={onClick} /> <MiniButton noHover noPadding icon={icon} disabled={disabled} onClick={onClick} />
<span id={id} className='min-w-[1.2rem]'> <span id={id} className={clsx({ 'text-xs': !isSmall }, textClassName)}>
{value} {value}
</span> </span>
</div> </div>
); );
} }
export default IconValue; export default ValueIcon;

View File

@ -2,14 +2,14 @@ import clsx from 'clsx';
import { CProps } from '../props'; import { CProps } from '../props';
interface LabeledValueProps extends CProps.Styling { interface ValueLabeledProps extends CProps.Styling {
id?: string; id?: string;
label: string; label: string;
text: string | number; text: string | number;
title?: string; title?: string;
} }
function LabeledValue({ id, label, text, title, className, ...restProps }: LabeledValueProps) { function ValueLabeled({ id, label, text, title, className, ...restProps }: ValueLabeledProps) {
return ( return (
<div className={clsx('flex justify-between gap-6', className)} {...restProps}> <div className={clsx('flex justify-between gap-6', className)} {...restProps}>
<span title={title}>{label}</span> <span title={title}>{label}</span>
@ -18,4 +18,4 @@ function LabeledValue({ id, label, text, title, className, ...restProps }: Label
); );
} }
export default LabeledValue; export default ValueLabeled;

View File

@ -0,0 +1,16 @@
import { PARAMETER } from '@/utils/constants';
import { CProps } from '../props';
import ValueIcon from './ValueIcon';
interface ValueStatsProps extends CProps.Styling, CProps.Titled {
id: string;
icon: React.ReactNode;
value: string | number;
}
function ValueStats(props: ValueStatsProps) {
return <ValueIcon dense smallThreshold={PARAMETER.statSmallThreshold} textClassName='min-w-[1.4rem]' {...props} />;
}
export default ValueStats;

View File

@ -1,5 +1,5 @@
import Divider from '@/components/ui/Divider'; import Divider from '@/components/ui/Divider';
import LabeledValue from '@/components/ui/LabeledValue'; import ValueLabeled from '@/components/ui/ValueLabeled';
import { IOperationSchemaStats } from '@/models/oss'; import { IOperationSchemaStats } from '@/models/oss';
interface OssStatsProps { interface OssStatsProps {
@ -14,13 +14,13 @@ function OssStats({ stats }: OssStatsProps) {
<div className='flex flex-col sm:gap-1 sm:ml-6 sm:mt-8 sm:w-[16rem]'> <div className='flex flex-col sm:gap-1 sm:ml-6 sm:mt-8 sm:w-[16rem]'>
<Divider margins='my-2' className='sm:hidden' /> <Divider margins='my-2' className='sm:hidden' />
<LabeledValue id='count_all' label='Всего операций' text={stats.count_operations} /> <ValueLabeled id='count_all' label='Всего операций' text={stats.count_operations} />
<LabeledValue id='count_inputs' label='Загрузка' text={stats.count_inputs} /> <ValueLabeled id='count_inputs' label='Загрузка' text={stats.count_inputs} />
<LabeledValue id='count_synthesis' label='Синтез' text={stats.count_synthesis} /> <ValueLabeled id='count_synthesis' label='Синтез' text={stats.count_synthesis} />
<Divider margins='my-2' /> <Divider margins='my-2' />
<LabeledValue id='count_schemas' label='Прикрепленные схемы' text={stats.count_schemas} /> <ValueLabeled id='count_schemas' label='Прикрепленные схемы' text={stats.count_schemas} />
</div> </div>
); );
} }

View File

@ -79,7 +79,7 @@ function EditorConstituenta({ activeCst, isModified, setIsModified, onOpenEdit }
} }
return ( return (
<div className='overflow-y-auto' style={{ maxHeight: panelHeight }}> <div className='overflow-y-auto min-h-[20rem]' style={{ maxHeight: panelHeight }}>
<ToolbarConstituenta <ToolbarConstituenta
activeCst={activeCst} activeCst={activeCst}
disabled={disabled} disabled={disabled}

View File

@ -80,7 +80,7 @@ function ToolbarConstituenta({
<MiniButton <MiniButton
title='Создать конституенту после данной' title='Создать конституенту после данной'
icon={<IconNewItem size={'1.25rem'} className='icon-green' />} icon={<IconNewItem size={'1.25rem'} className='icon-green' />}
disabled={disabled} disabled={!controller.isContentEditable || controller.isProcessing}
onClick={() => controller.createCst(activeCst?.cst_type, false)} onClick={() => controller.createCst(activeCst?.cst_type, false)}
/> />
<MiniButton <MiniButton

View File

@ -4,7 +4,7 @@ import { useIntl } from 'react-intl';
import { IconDateCreate, IconDateUpdate, IconEditor, IconFolder, IconOwner } from '@/components/Icons'; import { IconDateCreate, IconDateUpdate, IconEditor, IconFolder, IconOwner } from '@/components/Icons';
import InfoUsers from '@/components/info/InfoUsers'; import InfoUsers from '@/components/info/InfoUsers';
import SelectUser from '@/components/select/SelectUser'; import SelectUser from '@/components/select/SelectUser';
import IconValue from '@/components/ui/IconValue'; import ValueIcon from '@/components/ui/ValueIcon';
import Overlay from '@/components/ui/Overlay'; import Overlay from '@/components/ui/Overlay';
import Tooltip from '@/components/ui/Tooltip'; import Tooltip from '@/components/ui/Tooltip';
import { useAccessMode } from '@/context/AccessModeContext'; import { useAccessMode } from '@/context/AccessModeContext';
@ -47,7 +47,7 @@ function EditorLibraryItem({ item, isModified, controller }: EditorLibraryItemPr
return ( return (
<div className='flex flex-col'> <div className='flex flex-col'>
<IconValue <ValueIcon
className='sm:mb-1 text-ellipsis max-w-[30rem]' className='sm:mb-1 text-ellipsis max-w-[30rem]'
icon={<IconFolder size='1.25rem' className='icon-primary' />} icon={<IconFolder size='1.25rem' className='icon-primary' />}
value={item.location} value={item.location}
@ -68,7 +68,7 @@ function EditorLibraryItem({ item, isModified, controller }: EditorLibraryItemPr
) : null} ) : null}
</Overlay> </Overlay>
) : null} ) : null}
<IconValue <ValueIcon
className='sm:mb-1' className='sm:mb-1'
icon={<IconOwner size='1.25rem' className='icon-primary' />} icon={<IconOwner size='1.25rem' className='icon-primary' />}
value={getUserLabel(item.owner)} value={getUserLabel(item.owner)}
@ -78,7 +78,7 @@ function EditorLibraryItem({ item, isModified, controller }: EditorLibraryItemPr
/> />
<div className='sm:mb-1 flex justify-between items-center'> <div className='sm:mb-1 flex justify-between items-center'>
<IconValue <ValueIcon
id='editor_stats' id='editor_stats'
dense dense
icon={<IconEditor size='1.25rem' className='icon-primary' />} icon={<IconEditor size='1.25rem' className='icon-primary' />}
@ -90,7 +90,7 @@ function EditorLibraryItem({ item, isModified, controller }: EditorLibraryItemPr
<InfoUsers items={item?.editors ?? []} prefix={prefixes.user_editors} /> <InfoUsers items={item?.editors ?? []} prefix={prefixes.user_editors} />
</Tooltip> </Tooltip>
<IconValue <ValueIcon
dense dense
disabled disabled
icon={<IconDateUpdate size='1.25rem' className='clr-text-green' />} icon={<IconDateUpdate size='1.25rem' className='clr-text-green' />}
@ -98,7 +98,7 @@ function EditorLibraryItem({ item, isModified, controller }: EditorLibraryItemPr
title='Дата обновления' title='Дата обновления'
/> />
<IconValue <ValueIcon
dense dense
disabled disabled
icon={<IconDateCreate size='1.25rem' className='clr-text-green' />} icon={<IconDateCreate size='1.25rem' className='clr-text-green' />}

View File

@ -49,7 +49,7 @@ function EditorRSFormCard({ isModified, onDestroy, setIsModified }: EditorRSForm
/> />
<AnimateFade <AnimateFade
onKeyDown={handleInput} onKeyDown={handleInput}
className={clsx('sm:w-fit sm:max-w-fit max-w-[32rem]', 'mx-auto ', 'flex flex-col sm:flex-row px-6')} className={clsx('md:w-fit md:max-w-fit max-w-[32rem]', 'mx-auto ', 'flex flex-col md:flex-row px-6')}
> >
<FlexColumn className='flex-shrink'> <FlexColumn className='flex-shrink'>
<FormRSForm id={globals.library_item_editor} isModified={isModified} setIsModified={setIsModified} /> <FormRSForm id={globals.library_item_editor} isModified={isModified} setIsModified={setIsModified} />

View File

@ -1,13 +1,15 @@
import { import {
IconChild, IconChild,
IconConvention,
IconDefinition,
IconPredecessor, IconPredecessor,
IconStatusError, IconStatusError,
IconStatusIncalculable, IconStatusIncalculable,
IconStatusOK, IconStatusOK,
IconStatusProperty IconStatusProperty,
IconTerminology
} from '@/components/Icons'; } from '@/components/Icons';
import IconValue from '@/components/ui/IconValue'; import ValueStats from '@/components/ui/ValueStats';
import LabeledValue from '@/components/ui/LabeledValue';
import { type IRSFormStats } from '@/models/rsform'; import { type IRSFormStats } from '@/models/rsform';
interface RSFormStatsProps { interface RSFormStatsProps {
@ -19,120 +21,119 @@ function RSFormStats({ stats }: RSFormStatsProps) {
return null; return null;
} }
return ( return (
<div className='flex flex-col mt-3 sm:gap-1 sm:ml-6 sm:mt-8 sm:w-[16rem]'> <div className='flex flex-col mt-3 md:gap-1 md:ml-6 md:mt-8 md:w-[18rem] w-[25rem]'>
<div className='grid grid-cols-4 gap-1 mb-3 justify-items-start sm:justify-items-end'> <div className='grid grid-cols-4 gap-1 mb-3 justify-items-end'>
<div className='col-span-2 text-left w-full flex gap-3 sm:pl-3'> <div className='col-span-2 w-fit flex gap-3'>
<span>Всего</span> <span>Всего</span>
<span>{stats.count_all}</span> <span>{stats.count_all}</span>
</div> </div>
<IconValue <ValueStats
id='count_owned' id='count_owned'
dense
icon={<IconPredecessor size='1.25rem' className='clr-text-primary' />} icon={<IconPredecessor size='1.25rem' className='clr-text-primary' />}
value={stats.count_all - stats.count_inherited} value={stats.count_all - stats.count_inherited}
title='Собственные' title='Собственные'
/> />
<IconValue <ValueStats
id='count_inherited' id='count_inherited'
dense
icon={<IconChild size='1.25rem' className='clr-text-primary' />} icon={<IconChild size='1.25rem' className='clr-text-primary' />}
value={stats.count_inherited} value={stats.count_inherited}
title='Наследованные' title='Наследованные'
/> />
<IconValue <ValueStats
className='col-start-1' className='col-start-1'
id='count_ok' id='count_ok'
dense
icon={<IconStatusOK size='1.25rem' className='clr-text-green' />} icon={<IconStatusOK size='1.25rem' className='clr-text-green' />}
value={stats.count_all - stats.count_errors - stats.count_property - stats.count_incalculable} value={stats.count_all - stats.count_errors - stats.count_property - stats.count_incalculable}
title='Корректные' title='Корректные'
/> />
<IconValue <ValueStats
id='count_property' id='count_property'
dense
icon={<IconStatusProperty size='1.25rem' className='clr-text-primary' />} icon={<IconStatusProperty size='1.25rem' className='clr-text-primary' />}
value={stats.count_errors} value={stats.count_errors}
title='Неразмерные' title='Неразмерные'
/> />
<IconValue <ValueStats
id='count_incalculable' id='count_incalculable'
dense
icon={<IconStatusIncalculable size='1.25rem' className='clr-text-red' />} icon={<IconStatusIncalculable size='1.25rem' className='clr-text-red' />}
value={stats.count_incalculable} value={stats.count_incalculable}
title='Невычислимые' title='Невычислимые'
/> />
<IconValue <ValueStats
id='count_errors' id='count_errors'
dense
icon={<IconStatusError size='1.25rem' className='clr-text-red' />} icon={<IconStatusError size='1.25rem' className='clr-text-red' />}
value={stats.count_errors} value={stats.count_errors}
title='Некорректные' title='Некорректные'
/> />
<IconValue <ValueStats
id='count_base' id='count_base'
dense icon={<span className='font-math clr-text-default md:pr-1 pl-1 md:pl-0'>X</span>}
icon={<span className='font-math clr-text-default'>X#</span>}
value={stats.count_base} value={stats.count_base}
title='Базисные множества' title='Базисные множества'
/> />
<IconValue <ValueStats
id='count_constant' id='count_constant'
dense icon={<span className='font-math clr-text-default md:pr-1 pl-1 md:pl-0'>C</span>}
icon={<span className='font-math clr-text-default'>C#</span>}
value={stats.count_constant} value={stats.count_constant}
title='Константные множества' title='Константные множества'
/> />
<IconValue <ValueStats
id='count_structured' id='count_structured'
dense icon={<span className='font-math clr-text-default md:pr-1 pl-1 md:pl-0'>S</span>}
icon={<span className='font-math clr-text-default'>S#</span>}
value={stats.count_structured} value={stats.count_structured}
title='Родовые структуры' title='Родовые структуры'
/> />
<IconValue <ValueStats
id='count_axiom' id='count_axiom'
dense icon={<span className='font-math clr-text-default md:pr-1 pl-1 md:pl-0'>A</span>}
icon={<span className='font-math clr-text-default'>A#</span>}
value={stats.count_axiom} value={stats.count_axiom}
title='Аксиомы' title='Аксиомы'
/> />
<IconValue <ValueStats
id='count_term' id='count_term'
dense icon={<span className='font-math clr-text-default md:pr-1 pl-1 md:pl-0'>D</span>}
icon={<span className='font-math clr-text-default'>D#</span>}
value={stats.count_term} value={stats.count_term}
title='Термы' title='Термы'
/> />
<IconValue <ValueStats
id='count_function' id='count_function'
dense icon={<span className='font-math clr-text-default md:pr-1 pl-1 md:pl-0'>F</span>}
icon={<span className='font-math clr-text-default'>F#</span>}
value={stats.count_function} value={stats.count_function}
title='Терм-функции' title='Терм-функции'
/> />
<IconValue <ValueStats
id='count_predicate' id='count_predicate'
dense icon={<span className='font-math clr-text-default md:pr-1 pl-1 md:pl-0'>P</span>}
icon={<span className='font-math clr-text-default'>P#</span>}
value={stats.count_predicate} value={stats.count_predicate}
title='Предикат-функции' title='Предикат-функции'
/> />
<IconValue <ValueStats
id='count_theorem' id='count_theorem'
dense icon={<span className='font-math clr-text-default md:pr-1 pl-1 md:pl-0'>T</span>}
icon={<span className='font-math clr-text-default'>T#</span>}
value={stats.count_theorem} value={stats.count_theorem}
title='Теоремы' title='Теоремы'
/> />
</div>
<div className='sm:pl-3 max-w-[10rem] sm:max-w-[12rem]'> <ValueStats
<LabeledValue id='count_text_term' label='Термины' text={stats.count_text_term} /> id='count_text_term'
<LabeledValue id='count_definition' label='Определения' text={stats.count_definition} /> icon={<IconTerminology size='1.25rem' className='clr-text-primary' />}
<LabeledValue id='count_convention' label='Конвенции' text={stats.count_convention} /> value={stats.count_text_term}
title='Термины'
/>
<ValueStats
id='count_definition'
icon={<IconDefinition size='1.25rem' className='clr-text-primary' />}
value={stats.count_definition}
title='Определения'
/>
<ValueStats
id='count_convention'
icon={<IconConvention size='1.25rem' className='clr-text-primary' />}
value={stats.count_convention}
title='Конвенции'
/>
</div> </div>
</div> </div>
); );

View File

@ -30,6 +30,8 @@ export const PARAMETER = {
ossLongLabel: 14, // characters - threshold for long labels - small font ossLongLabel: 14, // characters - threshold for long labels - small font
ossTruncateLabel: 28, // characters - threshold for long labels - truncate ossTruncateLabel: 28, // characters - threshold for long labels - truncate
statSmallThreshold: 3, // characters - threshold for small labels - small font
logicLabel: 'LOGIC', logicLabel: 'LOGIC',
exteorVersion: '4.9.3', exteorVersion: '4.9.3',