Compare commits

...

8 Commits

Author SHA1 Message Date
Ivan
566c6be91d M: Improve OSS -> RSForm navigation and fix term graph
Some checks failed
Frontend CI / build (22.x) (push) Waiting to run
Backend CI / build (3.12) (push) Has been cancelled
2024-08-19 19:53:51 +03:00
Ivan
0c10162718 F: Enable tailwind universal styles optimization 2024-08-19 19:32:40 +03:00
Ivan
a6742a7b7c M: Improve typification and expression visibility 2024-08-19 19:15:21 +03:00
Ivan
02afd44488 M: Minor UI fixes and coverage fix 2024-08-19 18:32:21 +03:00
Ivan
13442c44aa B: Fix convention editing for OSS 2024-08-19 17:05:15 +03:00
Ivan
5cc7ee1353 B: Fix substitution editor 2024-08-19 12:32:52 +03:00
Ivan
f8f1ba4a62 M: Improve label visibility 2024-08-19 12:05:46 +03:00
Ivan
c6e64fddb8 M: Minor UI improvements 2024-08-19 10:55:52 +03:00
26 changed files with 101 additions and 64 deletions

View File

@ -557,8 +557,7 @@ class OperationSchema:
if old_data['term_forms'] == cst.term_forms: if old_data['term_forms'] == cst.term_forms:
new_data['term_forms'] = data['term_forms'] new_data['term_forms'] = data['term_forms']
if 'convention' in data: if 'convention' in data:
if old_data['convention'] == cst.convention: new_data['convention'] = data['convention']
new_data['convention'] = data['convention']
if 'definition_formal' in data: if 'definition_formal' in data:
new_data['definition_formal'] = replace_globals(data['definition_formal'], mapping) new_data['definition_formal'] = replace_globals(data['definition_formal'], mapping)
if 'term_raw' in data: if 'term_raw' in data:

View File

@ -92,7 +92,8 @@ class TestChangeConstituents(EndpointTester):
'item_data': { 'item_data': {
'term_raw': 'Test1', 'term_raw': 'Test1',
'definition_formal': r'X4\X4', 'definition_formal': r'X4\X4',
'definition_raw': '@{X5|sing,datv}' 'definition_raw': '@{X5|sing,datv}',
'convention': 'test'
} }
} }
response = self.executeOK(data=data, schema=self.ks1.model.pk) response = self.executeOK(data=data, schema=self.ks1.model.pk)
@ -102,8 +103,10 @@ class TestChangeConstituents(EndpointTester):
self.assertEqual(self.ks1X1.term_raw, data['item_data']['term_raw']) self.assertEqual(self.ks1X1.term_raw, data['item_data']['term_raw'])
self.assertEqual(self.ks1X1.definition_formal, data['item_data']['definition_formal']) self.assertEqual(self.ks1X1.definition_formal, data['item_data']['definition_formal'])
self.assertEqual(self.ks1X1.definition_raw, data['item_data']['definition_raw']) self.assertEqual(self.ks1X1.definition_raw, data['item_data']['definition_raw'])
self.assertEqual(self.ks1X1.convention, data['item_data']['convention'])
self.assertEqual(d2.definition_resolved, data['item_data']['term_raw']) self.assertEqual(d2.definition_resolved, data['item_data']['term_raw'])
self.assertEqual(inherited_cst.term_raw, data['item_data']['term_raw']) self.assertEqual(inherited_cst.term_raw, data['item_data']['term_raw'])
self.assertEqual(inherited_cst.convention, data['item_data']['convention'])
self.assertEqual(inherited_cst.definition_formal, r'X1\X1') self.assertEqual(inherited_cst.definition_formal, r'X1\X1')
self.assertEqual(inherited_cst.definition_raw, r'@{X2|sing,datv}') self.assertEqual(inherited_cst.definition_raw, r'@{X2|sing,datv}')

View File

@ -241,7 +241,11 @@ class RSForm:
old_data = {} old_data = {}
term_changed = False term_changed = False
if 'convention' in data: if 'convention' in data:
cst.convention = data['convention'] if cst.convention == data['convention']:
del data['convention']
else:
old_data['convention'] = cst.convention
cst.convention = data['convention']
if 'definition_formal' in data: if 'definition_formal' in data:
if cst.definition_formal == data['definition_formal']: if cst.definition_formal == data['definition_formal']:
del data['definition_formal'] del data['definition_formal']

View File

@ -23,14 +23,14 @@ function InfoConstituenta({ data, className, ...restProps }: InfoConstituentaPro
{data.term_resolved || data.term_raw} {data.term_resolved || data.term_raw}
</p> </p>
) : null} ) : null}
<p> <p className='break-all'>
<b>Типизация: </b> <b>Типизация: </b>
{labelCstTypification(data)} <span className='font-math'>{labelCstTypification(data)}</span>
</p> </p>
{data.definition_formal ? ( {data.definition_formal ? (
<p> <p>
<b>Выражение: </b> <b>Выражение: </b>
{data.definition_formal} <span className='font-math'>{data.definition_formal}</span>
</p> </p>
) : null} ) : null}
{data.definition_resolved ? ( {data.definition_resolved ? (

View File

@ -159,7 +159,7 @@ function PickSubstitutions({
id: 'right_alias', id: 'right_alias',
size: 65, size: 65,
cell: props => ( cell: props => (
<BadgeConstituenta theme={colors} value={props.row.original.original} prefixID={`${prefixID}_1_`} /> <BadgeConstituenta theme={colors} value={props.row.original.original} prefixID={`${prefixID}_2_`} />
) )
}), }),
columnHelper.accessor(item => item.original_source.alias, { columnHelper.accessor(item => item.original_source.alias, {

View File

@ -1,7 +1,7 @@
import clsx from 'clsx'; import clsx from 'clsx';
import { globals } from '@/utils/constants'; import { globals } from '@/utils/constants';
import { truncateText } from '@/utils/utils'; import { truncateToLastWord } from '@/utils/utils';
import { CProps } from '../props'; import { CProps } from '../props';
@ -11,7 +11,7 @@ export interface TextContentProps extends CProps.Styling {
} }
function TextContent({ className, text, maxLength, ...restProps }: TextContentProps) { function TextContent({ className, text, maxLength, ...restProps }: TextContentProps) {
const truncated = maxLength ? truncateText(text, maxLength) : text; const truncated = maxLength ? truncateToLastWord(text, maxLength) : text;
const isTruncated = maxLength && text.length > maxLength; const isTruncated = maxLength && text.length > maxLength;
return ( return (
<div <div

View File

@ -14,7 +14,6 @@ import { useLibrary } from '@/context/LibraryContext';
import { ILibraryItem, LibraryItemID, LibraryItemType } from '@/models/library'; import { ILibraryItem, LibraryItemID, LibraryItemType } from '@/models/library';
import { IOperationSchema } from '@/models/oss'; import { IOperationSchema } from '@/models/oss';
import { sortItemsForOSS } from '@/models/ossAPI'; import { sortItemsForOSS } from '@/models/ossAPI';
import { limits, patterns } from '@/utils/constants';
interface TabInputOperationProps { interface TabInputOperationProps {
oss: IOperationSchema; oss: IOperationSchema;
@ -67,8 +66,6 @@ function TabInputOperation({
id='operation_alias' id='operation_alias'
label='Сокращение' label='Сокращение'
className='w-[14rem]' className='w-[14rem]'
pattern={patterns.library_alias}
title={`не более ${limits.library_alias_len} символов`}
value={alias} value={alias}
onChange={event => setAlias(event.target.value)} onChange={event => setAlias(event.target.value)}
disabled={attachedID !== undefined} disabled={attachedID !== undefined}

View File

@ -4,7 +4,6 @@ import TextArea from '@/components/ui/TextArea';
import TextInput from '@/components/ui/TextInput'; import TextInput from '@/components/ui/TextInput';
import AnimateFade from '@/components/wrap/AnimateFade'; import AnimateFade from '@/components/wrap/AnimateFade';
import { IOperationSchema, OperationID } from '@/models/oss'; import { IOperationSchema, OperationID } from '@/models/oss';
import { limits, patterns } from '@/utils/constants';
import PickMultiOperation from '../../components/select/PickMultiOperation'; import PickMultiOperation from '../../components/select/PickMultiOperation';
@ -44,8 +43,6 @@ function TabSynthesisOperation({
id='operation_alias' id='operation_alias'
label='Сокращение' label='Сокращение'
className='w-[14rem]' className='w-[14rem]'
pattern={patterns.library_alias}
title={`не более ${limits.library_alias_len} символов`}
value={alias} value={alias}
onChange={event => setAlias(event.target.value)} onChange={event => setAlias(event.target.value)}
/> />

View File

@ -45,6 +45,7 @@ function DlgDeleteOperation({ hideWindow, target, onSubmit }: DlgDeleteOperation
titleHtml='Наследованные конституенты <br/>превратятся в дописанные' titleHtml='Наследованные конституенты <br/>превратятся в дописанные'
value={keepConstituents} value={keepConstituents}
setValue={setKeepConstituents} setValue={setKeepConstituents}
disabled={target.result === null}
/> />
<Checkbox <Checkbox
label='Удалить схему' label='Удалить схему'
@ -55,7 +56,7 @@ function DlgDeleteOperation({ hideWindow, target, onSubmit }: DlgDeleteOperation
} }
value={deleteSchema} value={deleteSchema}
setValue={setDeleteSchema} setValue={setDeleteSchema}
disabled={!target.is_owned || target.result === undefined} disabled={!target.is_owned || target.result === null}
/> />
</Modal> </Modal>
); );

View File

@ -67,8 +67,8 @@ function DlgEditOperation({ hideWindow, oss, target, onSubmit }: DlgEditOperatio
if (cache.loading || schemas.length !== schemasIDs.length) { if (cache.loading || schemas.length !== schemasIDs.length) {
return; return;
} }
setSubstitutions(() => setSubstitutions(prev =>
target.substitutions.filter(sub => { prev.filter(sub => {
const original = cache.getSchemaByCst(sub.original); const original = cache.getSchemaByCst(sub.original);
if (!original || !schemasIDs.includes(original.id)) { if (!original || !schemasIDs.includes(original.id)) {
return false; return false;
@ -80,7 +80,7 @@ function DlgEditOperation({ hideWindow, oss, target, onSubmit }: DlgEditOperatio
return true; return true;
}) })
); );
}, [schemasIDs, schemas, cache.loading, target.substitutions]); }, [schemasIDs, schemas, cache.loading]);
const handleSubmit = () => { const handleSubmit = () => {
const data: IOperationUpdateData = { const data: IOperationUpdateData = {

View File

@ -1,7 +1,6 @@
import TextArea from '@/components/ui/TextArea'; import TextArea from '@/components/ui/TextArea';
import TextInput from '@/components/ui/TextInput'; import TextInput from '@/components/ui/TextInput';
import AnimateFade from '@/components/wrap/AnimateFade'; import AnimateFade from '@/components/wrap/AnimateFade';
import { limits, patterns } from '@/utils/constants';
interface TabOperationProps { interface TabOperationProps {
alias: string; alias: string;
@ -26,8 +25,6 @@ function TabOperation({ alias, setAlias, title, setTitle, comment, setComment }:
id='operation_alias' id='operation_alias'
label='Сокращение' label='Сокращение'
className='w-[14rem]' className='w-[14rem]'
pattern={patterns.library_alias}
title={`не более ${limits.library_alias_len} символов`}
value={alias} value={alias}
onChange={event => setAlias(event.target.value)} onChange={event => setAlias(event.target.value)}
/> />

View File

@ -26,7 +26,7 @@ import useLocalStorage from '@/hooks/useLocalStorage';
import { AccessPolicy, LibraryItemType, LocationHead } from '@/models/library'; import { AccessPolicy, LibraryItemType, LocationHead } from '@/models/library';
import { ILibraryCreateData } from '@/models/library'; import { ILibraryCreateData } from '@/models/library';
import { combineLocation, validateLocation } from '@/models/libraryAPI'; import { combineLocation, validateLocation } from '@/models/libraryAPI';
import { EXTEOR_TRS_FILE, limits, patterns, storage } from '@/utils/constants'; import { EXTEOR_TRS_FILE, storage } from '@/utils/constants';
import { information } from '@/utils/labels'; import { information } from '@/utils/labels';
function FormCreateItem() { function FormCreateItem() {
@ -153,8 +153,6 @@ function FormCreateItem() {
label='Сокращение' label='Сокращение'
placeholder={file && 'Загрузить из файла'} placeholder={file && 'Загрузить из файла'}
className='w-[14rem]' className='w-[14rem]'
pattern={patterns.library_alias}
title={`не более ${limits.library_alias_len} символов`}
value={alias} value={alias}
onChange={event => setAlias(event.target.value)} onChange={event => setAlias(event.target.value)}
/> />

View File

@ -11,7 +11,6 @@ import TextInput from '@/components/ui/TextInput';
import { useOSS } from '@/context/OssContext'; import { useOSS } from '@/context/OssContext';
import { ILibraryUpdateData, LibraryItemType } from '@/models/library'; import { ILibraryUpdateData, LibraryItemType } from '@/models/library';
import ToolbarItemAccess from '@/pages/RSFormPage/EditorRSFormCard/ToolbarItemAccess'; import ToolbarItemAccess from '@/pages/RSFormPage/EditorRSFormCard/ToolbarItemAccess';
import { limits, patterns } from '@/utils/constants';
import { information } from '@/utils/labels'; import { information } from '@/utils/labels';
import { useOssEdit } from '../OssEditContext'; import { useOssEdit } from '../OssEditContext';
@ -102,8 +101,6 @@ function FormOSS({ id, isModified, setIsModified }: FormOSSProps) {
required required
label='Сокращение' label='Сокращение'
className='w-[14rem]' className='w-[14rem]'
pattern={patterns.library_alias}
title={`не более ${limits.library_alias_len} символов`}
disabled={!controller.isMutable} disabled={!controller.isMutable}
value={alias} value={alias}
onChange={event => setAlias(event.target.value)} onChange={event => setAlias(event.target.value)}

View File

@ -5,13 +5,17 @@ import TooltipOperation from '@/components/info/TooltipOperation';
import MiniButton from '@/components/ui/MiniButton.tsx'; import MiniButton from '@/components/ui/MiniButton.tsx';
import Overlay from '@/components/ui/Overlay'; import Overlay from '@/components/ui/Overlay';
import { OssNodeInternal } from '@/models/miscellaneous'; import { OssNodeInternal } from '@/models/miscellaneous';
import { prefixes } from '@/utils/constants'; import { PARAMETER, prefixes } from '@/utils/constants';
import { truncateToLastWord } from '@/utils/utils';
import { useOssEdit } from '../OssEditContext'; import { useOssEdit } from '../OssEditContext';
function InputNode(node: OssNodeInternal) { function InputNode(node: OssNodeInternal) {
const controller = useOssEdit(); const controller = useOssEdit();
const hasFile = !!node.data.operation.result; const hasFile = !!node.data.operation.result;
const longLabel = node.data.label.length > PARAMETER.ossLongLabel;
const labelText = truncateToLastWord(node.data.label, PARAMETER.ossTruncateLabel);
const handleOpenSchema = () => { const handleOpenSchema = () => {
controller.openOperationSchema(Number(node.id)); controller.openOperationSchema(Number(node.id));
@ -21,7 +25,7 @@ function InputNode(node: OssNodeInternal) {
<> <>
<Handle type='source' position={Position.Bottom} /> <Handle type='source' position={Position.Bottom} />
<Overlay position='top-0 right-0' className='flex'> <Overlay position='top-0 right-0' className='flex p-[0.1rem]'>
<MiniButton <MiniButton
icon={<IconRSForm className={hasFile ? 'clr-text-green' : 'clr-text-red'} size='0.75rem' />} icon={<IconRSForm className={hasFile ? 'clr-text-green' : 'clr-text-red'} size='0.75rem' />}
noHover noHover
@ -41,8 +45,18 @@ function InputNode(node: OssNodeInternal) {
</Overlay> </Overlay>
) : null} ) : null}
<div id={`${prefixes.operation_list}${node.id}`} className='flex-grow text-center'> <div id={`${prefixes.operation_list}${node.id}`} className='h-[34px] w-[144px] flex items-center justify-center'>
{node.data.label} <div
className='text-center'
style={{
fontSize: longLabel ? '12px' : '14px',
lineHeight: longLabel ? '16px' : '20px',
paddingLeft: '4px',
paddingRight: longLabel ? '10px' : '4px'
}}
>
{labelText}
</div>
{controller.showTooltip && !node.dragging ? ( {controller.showTooltip && !node.dragging ? (
<TooltipOperation anchor={`#${prefixes.operation_list}${node.id}`} node={node} /> <TooltipOperation anchor={`#${prefixes.operation_list}${node.id}`} node={node} />
) : null} ) : null}

View File

@ -1,3 +1,5 @@
'use client';
import { Handle, Position } from 'reactflow'; import { Handle, Position } from 'reactflow';
import { IconConsolidation, IconRSForm } from '@/components/Icons'; import { IconConsolidation, IconRSForm } from '@/components/Icons';
@ -5,7 +7,8 @@ import TooltipOperation from '@/components/info/TooltipOperation';
import MiniButton from '@/components/ui/MiniButton.tsx'; import MiniButton from '@/components/ui/MiniButton.tsx';
import Overlay from '@/components/ui/Overlay'; import Overlay from '@/components/ui/Overlay';
import { OssNodeInternal } from '@/models/miscellaneous'; import { OssNodeInternal } from '@/models/miscellaneous';
import { prefixes } from '@/utils/constants'; import { PARAMETER, prefixes } from '@/utils/constants';
import { truncateToLastWord } from '@/utils/utils';
import { useOssEdit } from '../OssEditContext'; import { useOssEdit } from '../OssEditContext';
@ -13,6 +16,8 @@ function OperationNode(node: OssNodeInternal) {
const controller = useOssEdit(); const controller = useOssEdit();
const hasFile = !!node.data.operation.result; const hasFile = !!node.data.operation.result;
const longLabel = node.data.label.length > PARAMETER.ossLongLabel;
const labelText = truncateToLastWord(node.data.label, PARAMETER.ossTruncateLabel);
const handleOpenSchema = () => { const handleOpenSchema = () => {
controller.openOperationSchema(Number(node.id)); controller.openOperationSchema(Number(node.id));
@ -22,14 +27,9 @@ function OperationNode(node: OssNodeInternal) {
<> <>
<Handle type='source' position={Position.Bottom} /> <Handle type='source' position={Position.Bottom} />
<Overlay position='top-0 right-0' className='flex flex-col gap-1'> <Overlay position='top-0 right-0' className='flex flex-col gap-1 p-[0.1rem]'>
<MiniButton <MiniButton
icon={ icon={<IconRSForm className={hasFile ? 'clr-text-green' : 'clr-text-red'} size='0.75rem' />}
<IconRSForm
className={hasFile ? 'clr-text-green' : 'clr-text-red'}
size={node.data.operation.is_consolidation ? '0.6rem' : '0.75rem'}
/>
}
noHover noHover
noPadding noPadding
title={hasFile ? 'Связанная КС' : 'Нет связанной КС'} title={hasFile ? 'Связанная КС' : 'Нет связанной КС'}
@ -39,7 +39,7 @@ function OperationNode(node: OssNodeInternal) {
/> />
{node.data.operation.is_consolidation ? ( {node.data.operation.is_consolidation ? (
<MiniButton <MiniButton
icon={<IconConsolidation className='clr-text-primary' size='0.6rem' />} icon={<IconConsolidation className='clr-text-primary' size='0.75rem' />}
disabled disabled
noPadding noPadding
noHover noHover
@ -55,8 +55,18 @@ function OperationNode(node: OssNodeInternal) {
</Overlay> </Overlay>
) : null} ) : null}
<div id={`${prefixes.operation_list}${node.id}`} className='flex-grow text-center'> <div id={`${prefixes.operation_list}${node.id}`} className='h-[34px] w-[144px] flex items-center justify-center'>
{node.data.label} <div
className='px-1 text-center'
style={{
fontSize: longLabel ? '12px' : '14px',
lineHeight: longLabel ? '16px' : '20px',
paddingLeft: '4px',
paddingRight: longLabel ? '10px' : '4px'
}}
>
{labelText}
</div>
{controller.showTooltip && !node.dragging ? ( {controller.showTooltip && !node.dragging ? (
<TooltipOperation anchor={`#${prefixes.operation_list}${node.id}`} node={node} /> <TooltipOperation anchor={`#${prefixes.operation_list}${node.id}`} node={node} />
) : null} ) : null}

View File

@ -335,7 +335,6 @@ function OssFlow({ isModified, setIsModified }: OssFlowProps) {
} }
} }
const canvasWidth = useMemo(() => 'calc(100vw - 1rem)', []);
const canvasHeight = useMemo(() => calculateHeight('1.75rem + 4px'), [calculateHeight]); const canvasHeight = useMemo(() => calculateHeight('1.75rem + 4px'), [calculateHeight]);
const OssNodeTypes: NodeTypes = useMemo( const OssNodeTypes: NodeTypes = useMemo(
@ -358,7 +357,7 @@ function OssFlow({ isModified, setIsModified }: OssFlowProps) {
fitView fitView
nodeTypes={OssNodeTypes} nodeTypes={OssNodeTypes}
maxZoom={2} maxZoom={2}
minZoom={0.75} minZoom={0.5}
nodesConnectable={false} nodesConnectable={false}
snapToGrid={true} snapToGrid={true}
snapGrid={[PARAMETER.ossGridSize, PARAMETER.ossGridSize]} snapGrid={[PARAMETER.ossGridSize, PARAMETER.ossGridSize]}
@ -413,7 +412,7 @@ function OssFlow({ isModified, setIsModified }: OssFlowProps) {
{...menuProps} {...menuProps}
/> />
) : null} ) : null}
<div className='relative' style={{ height: canvasHeight, width: canvasWidth }}> <div className='relative w-[100vw]' style={{ height: canvasHeight }}>
{graph} {graph}
</div> </div>
</AnimateFade> </AnimateFade>

View File

@ -33,6 +33,8 @@ import { UserID, UserLevel } from '@/models/user';
import { PARAMETER } from '@/utils/constants'; import { PARAMETER } from '@/utils/constants';
import { errors, information } from '@/utils/labels'; import { errors, information } from '@/utils/labels';
import { RSTabID } from '../RSFormPage/RSTabs';
export interface ICreateOperationPrompt { export interface ICreateOperationPrompt {
x: number; x: number;
y: number; y: number;
@ -206,7 +208,7 @@ export const OssEditState = ({ selected, setSelected, children }: OssEditStatePr
if (!node?.result) { if (!node?.result) {
return; return;
} }
router.push(urls.schema(node.result)); router.push(urls.schema_props({ id: node.result, tab: RSTabID.CST_LIST }));
}, },
[router, model] [router, model]
); );

View File

@ -220,7 +220,7 @@ function FormConstituenta({
label={isBasic ? 'Конвенция' : 'Комментарий'} label={isBasic ? 'Конвенция' : 'Комментарий'}
placeholder={isBasic ? 'Договоренность об интерпретации' : 'Пояснение разработчика'} placeholder={isBasic ? 'Договоренность об интерпретации' : 'Пояснение разработчика'}
value={convention} value={convention}
disabled={disabled} disabled={disabled || (isBasic && state?.is_inherited)}
rows={convention.length > 2 * ROW_SIZE_IN_CHARACTERS || convention.includes('\n') ? 4 : 2} rows={convention.length > 2 * ROW_SIZE_IN_CHARACTERS || convention.includes('\n') ? 4 : 2}
onChange={event => setConvention(event.target.value)} onChange={event => setConvention(event.target.value)}
/> />

View File

@ -10,7 +10,6 @@ import SubmitButton from '@/components/ui/SubmitButton';
import TextArea from '@/components/ui/TextArea'; import TextArea from '@/components/ui/TextArea';
import TextInput from '@/components/ui/TextInput'; import TextInput from '@/components/ui/TextInput';
import { ILibraryUpdateData, LibraryItemType } from '@/models/library'; import { ILibraryUpdateData, LibraryItemType } from '@/models/library';
import { limits, patterns } from '@/utils/constants';
import { useRSEdit } from '../RSEditContext'; import { useRSEdit } from '../RSEditContext';
import ToolbarItemAccess from './ToolbarItemAccess'; import ToolbarItemAccess from './ToolbarItemAccess';
@ -102,8 +101,6 @@ function FormRSForm({ id, isModified, setIsModified }: FormRSFormProps) {
required required
label='Сокращение' label='Сокращение'
className='w-[14rem]' className='w-[14rem]'
pattern={patterns.library_alias}
title={`не более ${limits.library_alias_len} символов`}
disabled={!controller.isContentEditable} disabled={!controller.isContentEditable}
value={alias} value={alias}
onChange={event => setAlias(event.target.value)} onChange={event => setAlias(event.target.value)}

View File

@ -12,8 +12,9 @@ import TextURL from '@/components/ui/TextURL';
import { useConceptOptions } from '@/context/ConceptOptionsContext'; import { useConceptOptions } from '@/context/ConceptOptionsContext';
import useWindowSize from '@/hooks/useWindowSize'; import useWindowSize from '@/hooks/useWindowSize';
import { ConstituentaID, IConstituenta } from '@/models/rsform'; import { ConstituentaID, IConstituenta } from '@/models/rsform';
import { prefixes } from '@/utils/constants'; import { PARAMETER, prefixes } from '@/utils/constants';
import { labelCstTypification } from '@/utils/labels'; import { labelCstTypification } from '@/utils/labels';
import { truncateToSymbol } from '@/utils/utils';
interface TableRSListProps { interface TableRSListProps {
items?: IConstituenta[]; items?: IConstituenta[];
@ -90,8 +91,13 @@ function TableRSList({
id: 'type', id: 'type',
header: 'Типизация', header: 'Типизация',
enableHiding: true, enableHiding: true,
size: 150,
minSize: 150,
maxSize: 200,
cell: props => ( cell: props => (
<div className={clsx('min-w-[9.3rem] max-w-[9.3rem]', 'text-sm break-words')}>{props.getValue()}</div> <div className={clsx('min-w-[9.3rem] max-w-[9.3rem]', 'text-xs break-words')}>
{truncateToSymbol(props.getValue(), PARAMETER.typificationTruncate)}
</div>
) )
}), }),
columnHelper.accessor(cst => cst.term_resolved || cst.term_raw || '', { columnHelper.accessor(cst => cst.term_resolved || cst.term_raw || '', {

View File

@ -99,8 +99,7 @@ function TermGraph({
); );
useLayoutEffect(() => { useLayoutEffect(() => {
graphRef.current?.resetControls(true); graphRef.current?.fitNodesInView([], { animated: true });
graphRef.current?.centerGraph();
}, [toggleResetView, graphRef]); }, [toggleResetView, graphRef]);
useLayoutEffect(() => { useLayoutEffect(() => {

View File

@ -66,7 +66,7 @@
border: 1px solid; border: 1px solid;
padding: 2px; padding: 2px;
width: 150px; width: 150px;
height: 30px; height: 40px;
font-size: 14px; font-size: 14px;
border-radius: 5px; border-radius: 5px;

View File

@ -25,6 +25,11 @@ export const PARAMETER = {
graphPopupDelay: 500, // milliseconds delay for graph popup selections graphPopupDelay: 500, // milliseconds delay for graph popup selections
graphRefreshDelay: 10, // milliseconds delay for graph viewpoint reset graphRefreshDelay: 10, // milliseconds delay for graph viewpoint reset
typificationTruncate: 42, // characters - threshold for long typification - truncate
ossLongLabel: 14, // characters - threshold for long labels - small font
ossTruncateLabel: 28, // characters - threshold for long labels - truncate
logicLabel: 'LOGIC', logicLabel: 'LOGIC',
exteorVersion: '4.9.3', exteorVersion: '4.9.3',
@ -35,7 +40,6 @@ export const PARAMETER = {
* Numeric limitations. * Numeric limitations.
*/ */
export const limits = { export const limits = {
library_alias_len: 12,
location_len: 500 location_len: 500
}; };
@ -48,8 +52,7 @@ export const EXTEOR_TRS_FILE = '.trs';
* Regex patterns for data validation. * Regex patterns for data validation.
*/ */
export const patterns = { export const patterns = {
login: '^[a-zA-Z][a-zA-Z0-9_\\-]{1,}[a-zA-Z0-9]$', login: '^[a-zA-Z][a-zA-Z0-9_\\-]{1,}[a-zA-Z0-9]$'
library_alias: `.{1,${limits.library_alias_len}}`
}; };
/** /**

View File

@ -67,9 +67,9 @@ export function applyPattern(text: string, mapping: Record<string, string>, patt
} }
/** /**
* Truncate text to max symbols. Add ellipsis if truncated. * Truncate text to last word up to max symbols. Add ellipsis if truncated.
*/ */
export function truncateText(text: string, maxSymbols: number): string { export function truncateToLastWord(text: string, maxSymbols: number): string {
if (text.length <= maxSymbols) { if (text.length <= maxSymbols) {
return text; return text;
} }
@ -81,6 +81,17 @@ export function truncateText(text: string, maxSymbols: number): string {
return trimmedText.slice(0, lastSpaceIndex) + '...'; return trimmedText.slice(0, lastSpaceIndex) + '...';
} }
/**
* Truncate text to max symbols. Add ellipsis if truncated.
*/
export function truncateToSymbol(text: string, maxSymbols: number): string {
if (text.length <= maxSymbols) {
return text;
}
const trimmedText = text.slice(0, maxSymbols);
return trimmedText + '...';
}
/** /**
* Check if Axios response is html. * Check if Axios response is html.
*/ */

View File

@ -16,5 +16,8 @@ export default {
}, },
extend: {} extend: {}
}, },
plugins: [] plugins: [],
experimental: {
optimizeUniversalDefaults: true
}
}; };

View File

@ -11,7 +11,7 @@ function BackendCoverage() {
$coverageExec = "$backend\venv\Scripts\coverage.exe" $coverageExec = "$backend\venv\Scripts\coverage.exe"
$djangoSrc = "$backend\manage.py" $djangoSrc = "$backend\manage.py"
$exclude = '*/venv/*,*/tests/*,*/migrations/*,*__init__.py,manage.py,apps.py,urls.py,settings.py' $exclude = '*/venv/*,*/tests/*,*/migrations/*,*__init__.py,shared/*,manage.py,apps.py,urls.py,settings.py,admin.py'
& $coverageExec run --omit=$exclude $djangoSrc test & $coverageExec run --omit=$exclude $djangoSrc test
& $coverageExec report & $coverageExec report