mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 13:00:39 +03:00
Minor UI improvements
This commit is contained in:
parent
04c3531f60
commit
a6b21279a9
|
@ -13,7 +13,7 @@ export interface CheckboxProps {
|
|||
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void
|
||||
}
|
||||
|
||||
function Checkbox({ id, required, disabled, tooltip, label, widthClass = 'w-full', value, onChange }: CheckboxProps) {
|
||||
function Checkbox({ id, required, disabled, tooltip, label, widthClass = 'w-fit', value, onChange }: CheckboxProps) {
|
||||
const inputRef = useRef<HTMLInputElement | null>(null);
|
||||
|
||||
const cursor = disabled ? 'cursor-not-allowed' : 'cursor-pointer';
|
||||
|
@ -23,11 +23,11 @@ function Checkbox({ id, required, disabled, tooltip, label, widthClass = 'w-full
|
|||
if (!disabled) {
|
||||
inputRef.current?.click();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
className={'flex gap-2 [&:not(:first-child)]:mt-3 ' + widthClass}
|
||||
className={'flex [&:not(:first-child)]:mt-3 clr-outline focus:outline-dotted focus:outline-1 ' + widthClass}
|
||||
title={tooltip}
|
||||
disabled={disabled}
|
||||
onClick={handleClick}
|
||||
|
@ -42,7 +42,7 @@ function Checkbox({ id, required, disabled, tooltip, label, widthClass = 'w-full
|
|||
/>
|
||||
{ label &&
|
||||
<Label
|
||||
className={`${cursor}`}
|
||||
className={`${cursor} px-2`}
|
||||
text={label}
|
||||
required={required}
|
||||
htmlFor={id}
|
||||
|
|
|
@ -15,7 +15,7 @@ interface FileInputProps {
|
|||
|
||||
function FileInput({ id, required, label, acceptType, widthClass = 'w-full', onChange }: FileInputProps) {
|
||||
const inputRef = useRef<HTMLInputElement | null>(null);
|
||||
const [labelText, setLabelText] = useState('Файл не выбран');
|
||||
const [labelText, setLabelText] = useState('');
|
||||
|
||||
const handleUploadClick = () => {
|
||||
inputRef.current?.click();
|
||||
|
@ -25,7 +25,7 @@ function FileInput({ id, required, label, acceptType, widthClass = 'w-full', onC
|
|||
if (event.target.files && event.target.files.length > 0) {
|
||||
setLabelText(event.target.files[0].name)
|
||||
} else {
|
||||
setLabelText('Файл не выбран')
|
||||
setLabelText('')
|
||||
}
|
||||
if (onChange) {
|
||||
onChange(event);
|
||||
|
|
|
@ -189,7 +189,9 @@
|
|||
}
|
||||
}
|
||||
|
||||
:is(.clr-outline
|
||||
:is(.clr-outline,
|
||||
.clr-btn-default,
|
||||
.clr-btn-primary
|
||||
):focus {
|
||||
outline-width: 2px;
|
||||
outline-style: solid;
|
||||
|
|
|
@ -94,7 +94,7 @@ function CreateRSFormPage() {
|
|||
value={common}
|
||||
onChange={event => setCommon(event.target.checked)}
|
||||
/>
|
||||
<FileInput id='trs' label='Загрузить *.trs'
|
||||
<FileInput id='trs' label='Загрузить из Экстеор'
|
||||
acceptType='.trs'
|
||||
onChange={handleFile}
|
||||
/>
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
import { useMemo } from 'react';
|
||||
import { darkTheme, GraphCanvas, GraphEdge, GraphNode, lightTheme } from 'reagraph';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { GraphCanvas,GraphEdge, GraphNode } from 'reagraph';
|
||||
|
||||
import Modal from '../../components/Common/Modal';
|
||||
import { useConceptTheme } from '../../context/ThemeContext';
|
||||
import { graphDarkT, graphLightT } from '../../utils/color';
|
||||
import { resources } from '../../utils/constants';
|
||||
import { SyntaxTree } from '../../utils/models';
|
||||
import { getNodeLabel } from '../../utils/staticUI';
|
||||
import { getASTNodeColor, getASTNodeLabel } from '../../utils/staticUI';
|
||||
|
||||
interface DlgShowASTProps {
|
||||
hideWindow: () => void
|
||||
|
@ -14,15 +15,21 @@ interface DlgShowASTProps {
|
|||
}
|
||||
|
||||
function DlgShowAST({ hideWindow, syntaxTree, expression }: DlgShowASTProps) {
|
||||
const { darkMode } = useConceptTheme();
|
||||
const { darkMode, colors } = useConceptTheme();
|
||||
const [hoverID, setHoverID] = useState<number | undefined>(undefined);
|
||||
const hoverNode = useMemo(
|
||||
() => {
|
||||
return syntaxTree.find(node => node.uid === hoverID);
|
||||
}, [hoverID, syntaxTree]);
|
||||
|
||||
const nodes: GraphNode[] = useMemo(
|
||||
() => syntaxTree.map(node => {
|
||||
return {
|
||||
id: String(node.uid),
|
||||
label: getNodeLabel(node)
|
||||
label: getASTNodeLabel(node),
|
||||
fill: getASTNodeColor(node, colors),
|
||||
};
|
||||
}), [syntaxTree]);
|
||||
}), [syntaxTree, colors]);
|
||||
|
||||
const edges: GraphEdge[] = useMemo(
|
||||
() => {
|
||||
|
@ -39,13 +46,32 @@ function DlgShowAST({ hideWindow, syntaxTree, expression }: DlgShowASTProps) {
|
|||
return result;
|
||||
}, [syntaxTree]);
|
||||
|
||||
const handleHoverIn = useCallback(
|
||||
(node: GraphNode) => {
|
||||
setHoverID(Number(node.id));
|
||||
}, []);
|
||||
|
||||
const handleHoverOut = useCallback(
|
||||
() => {
|
||||
setHoverID(undefined);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
hideWindow={hideWindow}
|
||||
readonly
|
||||
>
|
||||
<div className='flex flex-col items-start gap-2'>
|
||||
<div className='w-full text-lg text-center'>{expression}</div>
|
||||
<div className='w-full text-lg text-center'>
|
||||
{!hoverNode && expression}
|
||||
{hoverNode &&
|
||||
<div className='flex justify-center whitespace-pre'>
|
||||
<span>{expression.slice(0, hoverNode.start)}</span>
|
||||
<span className='clr-selected'>{expression.slice(hoverNode.start, hoverNode.finish)}</span>
|
||||
<span>{expression.slice(hoverNode.finish)}</span>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div className='flex-wrap w-full h-full overflow-auto'>
|
||||
<div className='relative w-[1040px] h-[600px] 2xl:w-[1680px] 2xl:h-[600px] max-h-full max-w-full'>
|
||||
<GraphCanvas
|
||||
|
@ -53,7 +79,9 @@ function DlgShowAST({ hideWindow, syntaxTree, expression }: DlgShowASTProps) {
|
|||
edges={edges}
|
||||
layoutType='hierarchicalTd'
|
||||
labelFontUrl={resources.graph_font}
|
||||
theme={darkMode ? darkTheme : lightTheme}
|
||||
theme={darkMode ? graphDarkT : graphLightT}
|
||||
onNodePointerOver={handleHoverIn}
|
||||
onNodePointerOut={handleHoverOut}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -222,7 +222,7 @@ function EditorRSExpression({
|
|||
{ !loading && parseData &&
|
||||
<ParsingResult
|
||||
data={parseData}
|
||||
onShowAST={ast => onShowAST(value, ast)}
|
||||
onShowAST={ast => onShowAST(getCstExpressionPrefix(activeCst!) + value, ast)}
|
||||
onShowError={onShowError}
|
||||
/>}
|
||||
{ !loading && !parseData &&
|
||||
|
|
|
@ -598,13 +598,14 @@ export function getRSErrorMessage(error: IRSErrorDescription): string {
|
|||
return 'UNKNOWN ERROR';
|
||||
}
|
||||
|
||||
export function getNodeLabel(node: ISyntaxTreeNode): string {
|
||||
export function getASTNodeLabel(node: ISyntaxTreeNode): string {
|
||||
switch(node.typeID) {
|
||||
case TokenID.ID_LOCAL: return node.data.value as string;
|
||||
case TokenID.ID_GLOBAL: return node.data.value as string;
|
||||
case TokenID.ID_FUNCTION: return node.data.value as string;
|
||||
case TokenID.ID_PREDICATE: return node.data.value as string;
|
||||
case TokenID.ID_RADICAL: return node.data.value as string;
|
||||
case TokenID.ID_LOCAL:
|
||||
case TokenID.ID_GLOBAL:
|
||||
case TokenID.ID_FUNCTION:
|
||||
case TokenID.ID_PREDICATE:
|
||||
case TokenID.ID_RADICAL:
|
||||
return node.data.value as string;
|
||||
|
||||
case TokenID.LIT_INTEGER: return String(node.data.value as number);
|
||||
|
||||
|
@ -675,3 +676,82 @@ export function getNodeLabel(node: ISyntaxTreeNode): string {
|
|||
// node
|
||||
return 'UNKNOWN ' + String(node.typeID);
|
||||
}
|
||||
|
||||
export function getASTNodeColor(node: ISyntaxTreeNode, colors: IColorTheme): string {
|
||||
switch(node.typeID) {
|
||||
case TokenID.PUNC_DEFINE:
|
||||
case TokenID.PUNC_STRUCT:
|
||||
case TokenID.ID_LOCAL:
|
||||
return colors.green;
|
||||
|
||||
case TokenID.ID_GLOBAL:
|
||||
case TokenID.ID_FUNCTION:
|
||||
case TokenID.ID_PREDICATE:
|
||||
case TokenID.ID_RADICAL:
|
||||
case TokenID.LIT_INTEGER:
|
||||
case TokenID.LIT_EMPTYSET:
|
||||
case TokenID.LIT_INTSET:
|
||||
return colors.teal;
|
||||
|
||||
case TokenID.FORALL:
|
||||
case TokenID.EXISTS:
|
||||
case TokenID.NOT:
|
||||
case TokenID.AND:
|
||||
case TokenID.OR:
|
||||
case TokenID.IMPLICATION:
|
||||
case TokenID.EQUIVALENT:
|
||||
case TokenID.GREATER:
|
||||
case TokenID.LESSER:
|
||||
case TokenID.EQUAL:
|
||||
case TokenID.NOTEQUAL:
|
||||
case TokenID.GREATER_OR_EQ:
|
||||
case TokenID.LESSER_OR_EQ:
|
||||
case TokenID.IN:
|
||||
case TokenID.NOTIN:
|
||||
case TokenID.SUBSET_OR_EQ:
|
||||
case TokenID.SUBSET:
|
||||
case TokenID.NOTSUBSET:
|
||||
return colors.orange;
|
||||
|
||||
case TokenID.NT_TUPLE:
|
||||
case TokenID.NT_ENUMERATION:
|
||||
case TokenID.BIGPR:
|
||||
case TokenID.SMALLPR:
|
||||
case TokenID.FILTER:
|
||||
case TokenID.PLUS:
|
||||
case TokenID.MINUS:
|
||||
case TokenID.MULTIPLY:
|
||||
case TokenID.BOOLEAN:
|
||||
case TokenID.DECART:
|
||||
case TokenID.INTERSECTION:
|
||||
case TokenID.UNION:
|
||||
case TokenID.SET_MINUS:
|
||||
case TokenID.SYMMINUS:
|
||||
case TokenID.REDUCE:
|
||||
case TokenID.CARD:
|
||||
case TokenID.BOOL:
|
||||
case TokenID.DEBOOL:
|
||||
return colors.blue;
|
||||
|
||||
case TokenID.NT_FUNC_DEFINITION:
|
||||
case TokenID.NT_DECLARATIVE_EXPR:
|
||||
case TokenID.NT_IMPERATIVE_EXPR:
|
||||
case TokenID.NT_RECURSIVE_FULL:
|
||||
case TokenID.NT_ENUM_DECL:
|
||||
case TokenID.NT_TUPLE_DECL:
|
||||
case TokenID.NT_ARG_DECL:
|
||||
case TokenID.NT_FUNC_CALL:
|
||||
case TokenID.NT_ARGUMENTS:
|
||||
case TokenID.NT_IMP_DECLARE:
|
||||
case TokenID.NT_IMP_ASSIGN:
|
||||
case TokenID.NT_IMP_LOGIC:
|
||||
case TokenID.NT_RECURSIVE_SHORT:
|
||||
return '';
|
||||
|
||||
case TokenID.PUNC_ASSIGN:
|
||||
case TokenID.PUNC_ITERATE:
|
||||
return colors.red;
|
||||
}
|
||||
// node
|
||||
return colors.red;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user