mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 04:50:36 +03:00
R: Redistribute constant and global dependencies
This commit is contained in:
parent
664fdb585c
commit
b29a4e7b49
|
@ -3,8 +3,6 @@
|
|||
import { createContext, useContext, useEffect, useState } from 'react';
|
||||
import { useNavigate } from 'react-router';
|
||||
|
||||
import { contextOutsideScope } from '@/utils/labels';
|
||||
|
||||
interface INavigationContext {
|
||||
push: (path: string, newTab?: boolean) => void;
|
||||
replace: (path: string) => void;
|
||||
|
@ -21,7 +19,7 @@ const NavigationContext = createContext<INavigationContext | null>(null);
|
|||
export const useConceptNavigation = () => {
|
||||
const context = useContext(NavigationContext);
|
||||
if (!context) {
|
||||
throw new Error(contextOutsideScope('useConceptNavigation', 'NavigationState'));
|
||||
throw new Error('useConceptNavigation has to be used within <NavigationState>');
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import { type Styling, type Titled } from '@/components/props';
|
||||
import { PARAMETER } from '@/utils/constants';
|
||||
|
||||
import { ValueIcon } from './ValueIcon';
|
||||
|
||||
// characters - threshold for small labels - small font
|
||||
const SMALL_THRESHOLD = 3;
|
||||
|
||||
interface ValueStatsProps extends Styling, Titled {
|
||||
/** Id of the component. */
|
||||
id: string;
|
||||
|
@ -18,5 +20,5 @@ interface ValueStatsProps extends Styling, Titled {
|
|||
* Displays statistics value with an icon.
|
||||
*/
|
||||
export function ValueStats(props: ValueStatsProps) {
|
||||
return <ValueIcon dense smallThreshold={PARAMETER.statSmallThreshold} textClassName='min-w-[1.4rem]' {...props} />;
|
||||
return <ValueIcon dense smallThreshold={SMALL_THRESHOLD} textClassName='min-w-[1.4rem]' {...props} />;
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@ import {
|
|||
isSetTypification
|
||||
} from '@/features/rsform/models/rslangAPI';
|
||||
|
||||
import { limits, PARAMETER } from '@/utils/constants';
|
||||
import { infoMsg } from '@/utils/labels';
|
||||
import { TextMatcher } from '@/utils/utils';
|
||||
|
||||
|
@ -24,6 +23,13 @@ import { describeSubstitutionError } from '../labels';
|
|||
import { type IOperation, type IOperationSchema, SubstitutionErrorType } from './oss';
|
||||
import { type Position2D } from './ossLayout';
|
||||
|
||||
export const GRID_SIZE = 10; // pixels - size of OSS grid
|
||||
const MIN_DISTANCE = 20; // pixels - minimum distance between node centers
|
||||
const DISTANCE_X = 180; // pixels - insert x-distance between node centers
|
||||
const DISTANCE_Y = 100; // pixels - insert y-distance between node centers
|
||||
|
||||
const STARTING_SUB_INDEX = 900; // max semantic index for starting substitution
|
||||
|
||||
/**
|
||||
* Checks if a given target {@link IOperation} matches the specified query using.
|
||||
*
|
||||
|
@ -92,7 +98,7 @@ export class SubstitutionValidator {
|
|||
this.schemaByCst.set(item.id, schema);
|
||||
});
|
||||
});
|
||||
let index = limits.max_semantic_index;
|
||||
let index = STARTING_SUB_INDEX;
|
||||
substitutions.forEach(item => {
|
||||
this.constituents.add(item.original);
|
||||
this.constituents.add(item.substitution);
|
||||
|
@ -500,27 +506,27 @@ export function calculateInsertPosition(
|
|||
}
|
||||
const maxX = Math.max(...inputsNodes.map(node => node.position_x));
|
||||
const minY = Math.min(...inputsNodes.map(node => node.position_y));
|
||||
result.x = maxX + PARAMETER.ossDistanceX;
|
||||
result.x = maxX + DISTANCE_X;
|
||||
result.y = minY;
|
||||
} else {
|
||||
const argNodes = positions.filter(pos => argumentsOps.includes(pos.id));
|
||||
const maxY = Math.max(...argNodes.map(node => node.position_y));
|
||||
const minX = Math.min(...argNodes.map(node => node.position_x));
|
||||
const maxX = Math.max(...argNodes.map(node => node.position_x));
|
||||
result.x = Math.ceil((maxX + minX) / 2 / PARAMETER.ossGridSize) * PARAMETER.ossGridSize;
|
||||
result.y = maxY + PARAMETER.ossDistanceY;
|
||||
result.x = Math.ceil((maxX + minX) / 2 / GRID_SIZE) * GRID_SIZE;
|
||||
result.y = maxY + DISTANCE_Y;
|
||||
}
|
||||
|
||||
let flagIntersect = false;
|
||||
do {
|
||||
flagIntersect = positions.some(
|
||||
position =>
|
||||
Math.abs(position.position_x - result.x) < PARAMETER.ossMinDistance &&
|
||||
Math.abs(position.position_y - result.y) < PARAMETER.ossMinDistance
|
||||
Math.abs(position.position_x - result.x) < MIN_DISTANCE &&
|
||||
Math.abs(position.position_y - result.y) < MIN_DISTANCE
|
||||
);
|
||||
if (flagIntersect) {
|
||||
result.x += PARAMETER.ossMinDistance;
|
||||
result.y += PARAMETER.ossMinDistance;
|
||||
result.x += MIN_DISTANCE;
|
||||
result.y += MIN_DISTANCE;
|
||||
}
|
||||
} while (flagIntersect);
|
||||
return result;
|
||||
|
|
|
@ -13,7 +13,6 @@ import {
|
|||
IconRSForm
|
||||
} from '@/components/Icons';
|
||||
import { useClickedOutside } from '@/hooks/useClickedOutside';
|
||||
import { PARAMETER } from '@/utils/constants';
|
||||
import { prepareTooltip } from '@/utils/utils';
|
||||
|
||||
import { OperationType } from '../../../backend/types';
|
||||
|
@ -21,6 +20,10 @@ import { useMutatingOss } from '../../../backend/useMutatingOss';
|
|||
import { type IOperation } from '../../../models/oss';
|
||||
import { useOssEdit } from '../OssEditContext';
|
||||
|
||||
// pixels - size of OSS context menu
|
||||
const MENU_WIDTH = 200;
|
||||
const MENU_HEIGHT = 200;
|
||||
|
||||
export interface ContextMenuData {
|
||||
operation: IOperation;
|
||||
cursorX: number;
|
||||
|
@ -116,8 +119,8 @@ export function NodeContextMenu({
|
|||
<div ref={ref} className='absolute select-none' style={{ top: cursorY, left: cursorX }}>
|
||||
<Dropdown
|
||||
isOpen={isOpen}
|
||||
stretchLeft={cursorX >= window.innerWidth - PARAMETER.ossContextMenuWidth}
|
||||
stretchTop={cursorY >= window.innerHeight - PARAMETER.ossContextMenuHeight}
|
||||
stretchLeft={cursorX >= window.innerWidth - MENU_WIDTH}
|
||||
stretchTop={cursorY >= window.innerHeight - MENU_HEIGHT}
|
||||
>
|
||||
<DropdownButton
|
||||
text='Редактировать'
|
||||
|
|
|
@ -29,6 +29,7 @@ import { useInputCreate } from '../../../backend/useInputCreate';
|
|||
import { useMutatingOss } from '../../../backend/useMutatingOss';
|
||||
import { useOperationExecute } from '../../../backend/useOperationExecute';
|
||||
import { useUpdatePositions } from '../../../backend/useUpdatePositions';
|
||||
import { GRID_SIZE } from '../../../models/ossAPI';
|
||||
import { type OssNode } from '../../../models/ossLayout';
|
||||
import { useOSSGraphStore } from '../../../stores/ossGraph';
|
||||
import { useOssEdit } from '../OssEditContext';
|
||||
|
@ -336,11 +337,11 @@ export function OssFlow() {
|
|||
minZoom={ZOOM_MIN}
|
||||
nodesConnectable={false}
|
||||
snapToGrid={true}
|
||||
snapGrid={[PARAMETER.ossGridSize, PARAMETER.ossGridSize]}
|
||||
snapGrid={[GRID_SIZE, GRID_SIZE]}
|
||||
onNodeContextMenu={handleContextMenu}
|
||||
onClick={handleCanvasClick}
|
||||
>
|
||||
{showGrid ? <Background gap={PARAMETER.ossGridSize} /> : null}
|
||||
{showGrid ? <Background gap={GRID_SIZE} /> : null}
|
||||
</ReactFlow>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -4,11 +4,14 @@ import { Overlay } from '@/components/Container';
|
|||
import { IconConsolidation, IconRSForm } from '@/components/Icons';
|
||||
import { Indicator } from '@/components/View';
|
||||
import { useTooltipsStore } from '@/stores/tooltips';
|
||||
import { globalIDs, PARAMETER } from '@/utils/constants';
|
||||
import { globalIDs } from '@/utils/constants';
|
||||
|
||||
import { OperationType } from '../../../../backend/types';
|
||||
import { type OssNodeInternal } from '../../../../models/ossLayout';
|
||||
|
||||
// characters - threshold for long labels - small font
|
||||
const LONG_LABEL_CHARS = 14;
|
||||
|
||||
interface NodeCoreProps {
|
||||
node: OssNodeInternal;
|
||||
}
|
||||
|
@ -17,7 +20,7 @@ export function NodeCore({ node }: NodeCoreProps) {
|
|||
const setHover = useTooltipsStore(state => state.setActiveOperation);
|
||||
|
||||
const hasFile = !!node.data.operation.result;
|
||||
const longLabel = node.data.label.length > PARAMETER.ossLongLabel;
|
||||
const longLabel = node.data.label.length > LONG_LABEL_CHARS;
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
import { urls, useConceptNavigation } from '@/app';
|
||||
import { useAuthSuspense } from '@/features/auth';
|
||||
import { useRoleStore, UserRole } from '@/features/users';
|
||||
import { describeUserRole, labelUserRole } from '@/features/users/labels';
|
||||
|
||||
import { Divider } from '@/components/Container';
|
||||
import { Button } from '@/components/Control';
|
||||
|
@ -21,7 +22,6 @@ import {
|
|||
IconReader,
|
||||
IconShare
|
||||
} from '@/components/Icons';
|
||||
import { describeAccessMode as describeUserRole, labelAccessMode as labelUserRole } from '@/utils/labels';
|
||||
import { sharePage } from '@/utils/utils';
|
||||
|
||||
import { useMutatingOss } from '../../backend/useMutatingOss';
|
||||
|
|
|
@ -49,6 +49,10 @@ import { ViewHidden } from './ViewHidden';
|
|||
const ZOOM_MAX = 3;
|
||||
const ZOOM_MIN = 0.25;
|
||||
|
||||
// ratio to client size used to determine which side of screen popup should be
|
||||
const HOVER_LIMIT_X = 0.4;
|
||||
const HOVER_LIMIT_Y = 0.6;
|
||||
|
||||
export function TGFlow() {
|
||||
const mainHeight = useMainHeight();
|
||||
const flow = useReactFlow();
|
||||
|
@ -288,8 +292,7 @@ export function TGFlow() {
|
|||
function handleNodeEnter(event: React.MouseEvent, cstID: number) {
|
||||
setHoverID(cstID);
|
||||
setHoverLeft(
|
||||
event.clientX / window.innerWidth >= PARAMETER.graphHoverXLimit ||
|
||||
event.clientY / window.innerHeight >= PARAMETER.graphHoverYLimit
|
||||
event.clientX / window.innerWidth >= HOVER_LIMIT_X || event.clientY / window.innerHeight >= HOVER_LIMIT_Y
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ import { useAuthSuspense } from '@/features/auth';
|
|||
import { AccessPolicy } from '@/features/library';
|
||||
import { LocationHead } from '@/features/library/models/library';
|
||||
import { useRoleStore, UserRole } from '@/features/users';
|
||||
import { describeUserRole, labelUserRole } from '@/features/users/labels';
|
||||
|
||||
import { Divider } from '@/components/Container';
|
||||
import { Button } from '@/components/Control';
|
||||
|
@ -39,7 +40,7 @@ import {
|
|||
import { useDialogsStore } from '@/stores/dialogs';
|
||||
import { useModificationStore } from '@/stores/modification';
|
||||
import { EXTEOR_TRS_FILE } from '@/utils/constants';
|
||||
import { describeAccessMode, labelAccessMode, tooltipText } from '@/utils/labels';
|
||||
import { tooltipText } from '@/utils/labels';
|
||||
import { generatePageQR, promptUnsaved, sharePage } from '@/utils/utils';
|
||||
|
||||
import { useDownloadRSForm } from '../../backend/useDownloadRSForm';
|
||||
|
@ -384,7 +385,7 @@ export function MenuRSTabs() {
|
|||
noBorder
|
||||
noOutline
|
||||
tabIndex={-1}
|
||||
title={`Режим ${labelAccessMode(role)}`}
|
||||
title={`Режим ${labelUserRole(role)}`}
|
||||
hideTitle={accessMenu.isOpen}
|
||||
className='h-full pr-2'
|
||||
icon={
|
||||
|
@ -402,28 +403,28 @@ export function MenuRSTabs() {
|
|||
/>
|
||||
<Dropdown isOpen={accessMenu.isOpen}>
|
||||
<DropdownButton
|
||||
text={labelAccessMode(UserRole.READER)}
|
||||
title={describeAccessMode(UserRole.READER)}
|
||||
text={labelUserRole(UserRole.READER)}
|
||||
title={describeUserRole(UserRole.READER)}
|
||||
icon={<IconReader size='1rem' className='icon-primary' />}
|
||||
onClick={() => handleChangeMode(UserRole.READER)}
|
||||
/>
|
||||
<DropdownButton
|
||||
text={labelAccessMode(UserRole.EDITOR)}
|
||||
title={describeAccessMode(UserRole.EDITOR)}
|
||||
text={labelUserRole(UserRole.EDITOR)}
|
||||
title={describeUserRole(UserRole.EDITOR)}
|
||||
icon={<IconEditor size='1rem' className='icon-primary' />}
|
||||
disabled={!isOwned && (!user.id || !schema.editors.includes(user.id))}
|
||||
onClick={() => handleChangeMode(UserRole.EDITOR)}
|
||||
/>
|
||||
<DropdownButton
|
||||
text={labelAccessMode(UserRole.OWNER)}
|
||||
title={describeAccessMode(UserRole.OWNER)}
|
||||
text={labelUserRole(UserRole.OWNER)}
|
||||
title={describeUserRole(UserRole.OWNER)}
|
||||
icon={<IconOwner size='1rem' className='icon-primary' />}
|
||||
disabled={!isOwned}
|
||||
onClick={() => handleChangeMode(UserRole.OWNER)}
|
||||
/>
|
||||
<DropdownButton
|
||||
text={labelAccessMode(UserRole.ADMIN)}
|
||||
title={describeAccessMode(UserRole.ADMIN)}
|
||||
text={labelUserRole(UserRole.ADMIN)}
|
||||
title={describeUserRole(UserRole.ADMIN)}
|
||||
icon={<IconAdmin size='1rem' className='icon-primary' />}
|
||||
disabled={!user.is_staff}
|
||||
onClick={() => handleChangeMode(UserRole.ADMIN)}
|
||||
|
|
31
rsconcept/frontend/src/features/users/labels.ts
Normal file
31
rsconcept/frontend/src/features/users/labels.ts
Normal file
|
@ -0,0 +1,31 @@
|
|||
import { UserRole } from './stores/role';
|
||||
|
||||
/**
|
||||
* Retrieves label for {@link UserRole}.
|
||||
*/
|
||||
export function labelUserRole(mode: UserRole): string {
|
||||
// prettier-ignore
|
||||
switch (mode) {
|
||||
case UserRole.READER: return 'Читатель';
|
||||
case UserRole.EDITOR: return 'Редактор';
|
||||
case UserRole.OWNER: return 'Владелец';
|
||||
case UserRole.ADMIN: return 'Администратор';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves description for {@link UserRole}.
|
||||
*/
|
||||
export function describeUserRole(mode: UserRole): string {
|
||||
// prettier-ignore
|
||||
switch (mode) {
|
||||
case UserRole.READER:
|
||||
return 'Режим запрещает редактирование';
|
||||
case UserRole.EDITOR:
|
||||
return 'Режим редактирования';
|
||||
case UserRole.OWNER:
|
||||
return 'Режим владельца';
|
||||
case UserRole.ADMIN:
|
||||
return 'Режим администратора';
|
||||
}
|
||||
}
|
|
@ -11,33 +11,22 @@ export const PARAMETER = {
|
|||
refreshTimeout: 100, // milliseconds delay for post-refresh actions
|
||||
minimalTimeout: 10, // milliseconds delay for fast updates
|
||||
zoomDuration: 500, // milliseconds animation duration
|
||||
moveDuration: 500, // milliseconds - duration of move animation
|
||||
dropdownDuration: 200, // milliseconds - duration of dropdown animation
|
||||
navigationDuration: 300, // milliseconds navigation duration
|
||||
navigationPopupDelay: 300, // milliseconds delay for navigation popup
|
||||
graphPopupDelay: 500, // milliseconds delay for graph popup selections
|
||||
graphRefreshDelay: 10, // milliseconds delay for graph viewpoint reset
|
||||
|
||||
fastAnimation: 200, // milliseconds - duration of fast animation
|
||||
fadeDuration: 300, // milliseconds - duration of fade animation
|
||||
dropdownDuration: 200, // milliseconds - duration of dropdown animation
|
||||
moveDuration: 500, // milliseconds - duration of move animation
|
||||
|
||||
ossImageWidth: 1280, // pixels - size of OSS image
|
||||
ossImageHeight: 960, // pixels - size of OSS image
|
||||
ossContextMenuWidth: 200, // pixels - width of OSS context menu
|
||||
ossContextMenuHeight: 200, // pixels - height of OSS context menu
|
||||
ossGridSize: 10, // pixels - size of OSS grid
|
||||
ossMinDistance: 20, // pixels - minimum distance between node centers
|
||||
ossDistanceX: 180, // pixels - insert x-distance between node centers
|
||||
ossDistanceY: 100, // pixels - insert y-distance between node centers
|
||||
|
||||
graphHandleSize: 3, // pixels - size of graph connection handle
|
||||
graphNodeRadius: 20, // pixels - radius of graph node
|
||||
graphNodePadding: 5, // pixels - padding of graph node
|
||||
graphHoverXLimit: 0.4, // ratio to clientWidth used to determine which side of screen popup should be
|
||||
graphHoverYLimit: 0.6, // ratio to clientHeight used to determine which side of screen popup should be
|
||||
graphPopupDelay: 500, // milliseconds delay for graph popup selections
|
||||
graphRefreshDelay: 10, // milliseconds delay for graph viewpoint reset
|
||||
|
||||
ossLongLabel: 14, // characters - threshold for long labels - small font
|
||||
statSmallThreshold: 3, // characters - threshold for small labels - small font
|
||||
|
||||
logicLabel: 'LOGIC',
|
||||
errorNodeLabel: '[ERROR]',
|
||||
|
@ -50,8 +39,7 @@ export const PARAMETER = {
|
|||
* Numeric limitations.
|
||||
*/
|
||||
export const limits = {
|
||||
location_len: 500,
|
||||
max_semantic_index: 900
|
||||
location_len: 500
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -133,12 +121,10 @@ export const prefixes = {
|
|||
operation_list: 'operation_list_',
|
||||
csttype_list: 'csttype_',
|
||||
policy_list: 'policy_list_',
|
||||
library_filters_list: 'library_filters_list_',
|
||||
location_head_list: 'location_head_list_',
|
||||
folders_list: 'folders_list_',
|
||||
topic_list: 'topic_list_',
|
||||
topic_item: 'topic_item_',
|
||||
library_list: 'library_list_',
|
||||
user_subs: 'user_subs_',
|
||||
user_editors: 'user_editors_',
|
||||
wordform_list: 'wordform_list_',
|
||||
|
|
|
@ -4,37 +4,6 @@
|
|||
* Label is a short text used to represent an entity.
|
||||
* Description is a long description used in tooltips.
|
||||
*/
|
||||
import { UserRole } from '@/features/users/stores/role';
|
||||
|
||||
/**
|
||||
* Retrieves label for {@link UserRole}.
|
||||
*/
|
||||
export function labelAccessMode(mode: UserRole): string {
|
||||
// prettier-ignore
|
||||
switch (mode) {
|
||||
case UserRole.READER: return 'Читатель';
|
||||
case UserRole.EDITOR: return 'Редактор';
|
||||
case UserRole.OWNER: return 'Владелец';
|
||||
case UserRole.ADMIN: return 'Администратор';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves description for {@link UserRole}.
|
||||
*/
|
||||
export function describeAccessMode(mode: UserRole): string {
|
||||
// prettier-ignore
|
||||
switch (mode) {
|
||||
case UserRole.READER:
|
||||
return 'Режим запрещает редактирование';
|
||||
case UserRole.EDITOR:
|
||||
return 'Режим редактирования';
|
||||
case UserRole.OWNER:
|
||||
return 'Режим владельца';
|
||||
case UserRole.ADMIN:
|
||||
return 'Режим администратора';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* UI info descriptors.
|
||||
|
@ -116,8 +85,3 @@ export const promptText = {
|
|||
ownerChange:
|
||||
'Вы уверены, что хотите изменить владельца? Вы потеряете право управления данной схемой. Данное действие отменить нельзя'
|
||||
};
|
||||
|
||||
// ============== INTERNAL LABELS FOR DEVELOPERS TEXT ================
|
||||
export function contextOutsideScope(contextName: string, contextState: string): string {
|
||||
return `${contextName} has to be used within <${contextState}>`;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user