mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 13:00:39 +03:00
Implement TemplateExpression editing
This commit is contained in:
parent
4cd8b31b59
commit
cefd0d3c40
30
rsconcept/frontend/src/components/Common/ConceptSearch.tsx
Normal file
30
rsconcept/frontend/src/components/Common/ConceptSearch.tsx
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import { MagnifyingGlassIcon } from '../Icons';
|
||||||
|
import TextInput from './TextInput';
|
||||||
|
|
||||||
|
interface ConceptSearchProps {
|
||||||
|
value: string
|
||||||
|
onChange?: (newValue: string) => void
|
||||||
|
dense?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
function ConceptSearch({ value, onChange, dense }: ConceptSearchProps) {
|
||||||
|
const borderClass = dense ? 'border-t border-x': '';
|
||||||
|
return (
|
||||||
|
<div className='relative'>
|
||||||
|
<div className='absolute inset-y-0 flex items-center pl-3 pointer-events-none text-controls'>
|
||||||
|
<MagnifyingGlassIcon />
|
||||||
|
</div>
|
||||||
|
<TextInput
|
||||||
|
dimensions={`w-full pl-10 ${borderClass} rounded`}
|
||||||
|
placeholder='Поиск'
|
||||||
|
noOutline
|
||||||
|
noBorder={dense}
|
||||||
|
value={value}
|
||||||
|
onChange={event => onChange && onChange(event.target.value)}
|
||||||
|
/>
|
||||||
|
</div>);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ConceptSearch;
|
||||||
|
|
||||||
|
|
|
@ -2,14 +2,16 @@ import type { TabProps } from 'react-tabs';
|
||||||
import { Tab } from 'react-tabs';
|
import { Tab } from 'react-tabs';
|
||||||
|
|
||||||
interface ConceptTabProps
|
interface ConceptTabProps
|
||||||
extends Omit<TabProps, 'className'> {
|
extends Omit<TabProps, 'className' | 'title'> {
|
||||||
className?: string
|
className?: string
|
||||||
|
tooltip?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
function ConceptTab({ children, className, ...otherProps }: ConceptTabProps) {
|
function ConceptTab({ children, tooltip, className, ...otherProps }: ConceptTabProps) {
|
||||||
return (
|
return (
|
||||||
<Tab
|
<Tab
|
||||||
className={`px-2 py-1 h-full text-sm hover:cursor-pointer clr-tab whitespace-nowrap ${className}`}
|
className={`px-2 py-1 h-full text-sm hover:cursor-pointer clr-tab whitespace-nowrap ${className}`}
|
||||||
|
title={tooltip}
|
||||||
{...otherProps}
|
{...otherProps}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
|
|
@ -40,18 +40,18 @@ function Modal({
|
||||||
<div className='fixed top-0 left-0 w-full h-full z-navigation clr-modal-backdrop' />
|
<div className='fixed top-0 left-0 w-full h-full z-navigation clr-modal-backdrop' />
|
||||||
<div
|
<div
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className='fixed bottom-1/2 left-1/2 translate-y-1/2 -translate-x-1/2 px-6 py-4 flex flex-col justify-start w-fit max-w-[calc(100vw-2rem)] h-fit z-modal clr-app border shadow-md'
|
className='fixed bottom-1/2 left-1/2 translate-y-1/2 -translate-x-1/2 px-6 py-3 flex flex-col justify-start w-fit max-w-[calc(100vw-2rem)] h-fit z-modal clr-app border shadow-md'
|
||||||
>
|
>
|
||||||
{ title && <h1 className='mb-2 text-xl select-none'>{title}</h1> }
|
{ title && <h1 className='pb-3 text-xl select-none'>{title}</h1> }
|
||||||
<div className='max-h-[calc(100vh-8rem)]'>
|
<div className='max-h-[calc(100vh-8rem)]'>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
<div className='flex justify-center w-full gap-4 pt-4 mt-2 border-t-2 z-modal-controls'>
|
<div className='flex justify-center w-full gap-4 pt-3 mt-2 border-t-2 z-modal-controls'>
|
||||||
{!readonly &&
|
{!readonly &&
|
||||||
<Button
|
<Button
|
||||||
text={submitText}
|
text={submitText}
|
||||||
tooltip={!canSubmit ? submitInvalidTooltip: ''}
|
tooltip={!canSubmit ? submitInvalidTooltip: ''}
|
||||||
dimensions='min-w-[6rem] min-h-[2.6rem] w-fit h-fit'
|
dimensions='min-w-[8rem] min-h-[2.6rem] w-fit h-fit'
|
||||||
colors='clr-btn-primary'
|
colors='clr-btn-primary'
|
||||||
disabled={!canSubmit}
|
disabled={!canSubmit}
|
||||||
onClick={handleSubmit}
|
onClick={handleSubmit}
|
||||||
|
@ -59,7 +59,7 @@ function Modal({
|
||||||
/>}
|
/>}
|
||||||
<Button
|
<Button
|
||||||
text={readonly ? 'Закрыть' : 'Отмена'}
|
text={readonly ? 'Закрыть' : 'Отмена'}
|
||||||
dimensions='min-w-[6rem] min-h-[2.6rem] w-fit h-fit'
|
dimensions='min-w-[8rem] min-h-[2.6rem] w-fit h-fit'
|
||||||
onClick={handleCancel}
|
onClick={handleCancel}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -12,15 +12,14 @@ function SubmitButton({
|
||||||
dimensions = 'w-fit h-fit'
|
dimensions = 'w-fit h-fit'
|
||||||
}: SubmitButtonProps) {
|
}: SubmitButtonProps) {
|
||||||
return (
|
return (
|
||||||
<button type='submit'
|
<button type='submit'
|
||||||
title={tooltip}
|
title={tooltip}
|
||||||
className={`px-4 py-2 inline-flex items-center gap-2 align-middle justify-center font-semibold select-none disabled:cursor-not-allowed border rounded clr-btn-primary ${dimensions} ${loading ? ' cursor-progress' : ''}`}
|
className={`px-4 py-2 inline-flex items-center gap-2 align-middle justify-center font-semibold select-none disabled:cursor-not-allowed border rounded clr-btn-primary ${dimensions} ${loading ? ' cursor-progress' : ''}`}
|
||||||
disabled={disabled ?? loading}
|
disabled={disabled ?? loading}
|
||||||
>
|
>
|
||||||
{icon && <span>{icon}</span>}
|
{icon && <span>{icon}</span>}
|
||||||
{text && <span>{text}</span>}
|
{text && <span>{text}</span>}
|
||||||
</button>
|
</button>);
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SubmitButton;
|
export default SubmitButton;
|
||||||
|
|
|
@ -16,16 +16,15 @@ function SwitchButton<ValueType>({
|
||||||
isSelected, onSelect, ...props
|
isSelected, onSelect, ...props
|
||||||
}: SwitchButtonProps<ValueType>) {
|
}: SwitchButtonProps<ValueType>) {
|
||||||
return (
|
return (
|
||||||
<button type='button' tabIndex={-1}
|
<button type='button' tabIndex={-1}
|
||||||
title={tooltip}
|
title={tooltip}
|
||||||
onClick={() => onSelect(value)}
|
onClick={() => onSelect(value)}
|
||||||
className={`px-2 py-1 border font-semibold small-caps rounded-none cursor-pointer clr-btn-clear clr-hover ${dimensions} ${isSelected ? 'clr-selected': ''}`}
|
className={`px-2 py-1 border font-semibold small-caps rounded-none cursor-pointer clr-btn-clear clr-hover ${dimensions} ${isSelected ? 'clr-selected': ''}`}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{icon && icon}
|
{icon && icon}
|
||||||
{label}
|
{label}
|
||||||
</button>
|
</button>);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SwitchButton;
|
export default SwitchButton;
|
||||||
|
|
|
@ -27,7 +27,8 @@ extends Pick<TableOptions<TData>,
|
||||||
> {
|
> {
|
||||||
dense?: boolean
|
dense?: boolean
|
||||||
headPosition?: string
|
headPosition?: string
|
||||||
noFooter?: boolean // Disables footer rendering
|
noHeader?: boolean
|
||||||
|
noFooter?: boolean
|
||||||
conditionalRowStyles?: IConditionalStyle<TData>[]
|
conditionalRowStyles?: IConditionalStyle<TData>[]
|
||||||
onRowClicked?: (rowData: TData, event: React.MouseEvent<Element, MouseEvent>) => void
|
onRowClicked?: (rowData: TData, event: React.MouseEvent<Element, MouseEvent>) => void
|
||||||
onRowDoubleClicked?: (rowData: TData, event: React.MouseEvent<Element, MouseEvent>) => void
|
onRowDoubleClicked?: (rowData: TData, event: React.MouseEvent<Element, MouseEvent>) => void
|
||||||
|
@ -55,7 +56,7 @@ extends Pick<TableOptions<TData>,
|
||||||
* No sticky header if omitted
|
* No sticky header if omitted
|
||||||
*/
|
*/
|
||||||
export default function DataTable<TData extends RowData>({
|
export default function DataTable<TData extends RowData>({
|
||||||
dense, headPosition, conditionalRowStyles, noFooter,
|
dense, headPosition, conditionalRowStyles, noFooter, noHeader,
|
||||||
onRowClicked, onRowDoubleClicked, noDataComponent,
|
onRowClicked, onRowDoubleClicked, noDataComponent,
|
||||||
|
|
||||||
enableRowSelection,
|
enableRowSelection,
|
||||||
|
@ -112,6 +113,7 @@ export default function DataTable<TData extends RowData>({
|
||||||
<div className='w-full'>
|
<div className='w-full'>
|
||||||
<div className='flex flex-col items-stretch'>
|
<div className='flex flex-col items-stretch'>
|
||||||
<table>
|
<table>
|
||||||
|
{ !noHeader &&
|
||||||
<thead
|
<thead
|
||||||
className={`clr-app shadow-border`}
|
className={`clr-app shadow-border`}
|
||||||
style={{
|
style={{
|
||||||
|
@ -147,7 +149,7 @@ export default function DataTable<TData extends RowData>({
|
||||||
))}
|
))}
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
))}
|
||||||
</thead>
|
</thead>}
|
||||||
|
|
||||||
<tbody>
|
<tbody>
|
||||||
{tableImpl.getRowModel().rows.map(
|
{tableImpl.getRowModel().rows.map(
|
||||||
|
|
|
@ -45,15 +45,16 @@ const editorSetup: BasicSetupOptions = {
|
||||||
|
|
||||||
interface RSInputProps
|
interface RSInputProps
|
||||||
extends Pick<ReactCodeMirrorProps,
|
extends Pick<ReactCodeMirrorProps,
|
||||||
'id'| 'editable' | 'height' | 'value' | 'className' | 'onFocus' | 'onBlur' | 'placeholder'
|
'id' | 'height' | 'value' | 'className' | 'onFocus' | 'onBlur' | 'placeholder'
|
||||||
> {
|
> {
|
||||||
label?: string
|
label?: string
|
||||||
|
disabled?: boolean
|
||||||
innerref?: RefObject<ReactCodeMirrorRef> | undefined
|
innerref?: RefObject<ReactCodeMirrorRef> | undefined
|
||||||
onChange?: (newValue: string) => void
|
onChange?: (newValue: string) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
function RSInput({
|
function RSInput({
|
||||||
id, label, innerref, onChange, editable,
|
id, label, innerref, onChange, disabled,
|
||||||
...props
|
...props
|
||||||
}: RSInputProps) {
|
}: RSInputProps) {
|
||||||
const { darkMode, colors } = useConceptTheme();
|
const { darkMode, colors } = useConceptTheme();
|
||||||
|
@ -65,13 +66,13 @@ function RSInput({
|
||||||
return innerref ?? internalRef;
|
return innerref ?? internalRef;
|
||||||
}, [internalRef, innerref]);
|
}, [internalRef, innerref]);
|
||||||
|
|
||||||
const cursor = useMemo(() => editable ? 'cursor-text': 'cursor-default', [editable]);
|
const cursor = useMemo(() => !disabled ? 'cursor-text': 'cursor-default', [disabled]);
|
||||||
const customTheme: Extension = useMemo(
|
const customTheme: Extension = useMemo(
|
||||||
() => createTheme({
|
() => createTheme({
|
||||||
theme: darkMode ? 'dark' : 'light',
|
theme: darkMode ? 'dark' : 'light',
|
||||||
settings: {
|
settings: {
|
||||||
fontFamily: 'inherit',
|
fontFamily: 'inherit',
|
||||||
background: editable ? colors.bgInput : colors.bgDefault,
|
background: !disabled ? colors.bgInput : colors.bgDefault,
|
||||||
foreground: colors.fgDefault,
|
foreground: colors.fgDefault,
|
||||||
selection: colors.bgHover
|
selection: colors.bgHover
|
||||||
},
|
},
|
||||||
|
@ -84,7 +85,7 @@ function RSInput({
|
||||||
{ tag: tags.controlKeyword, fontWeight: '500'}, // R | I | D
|
{ tag: tags.controlKeyword, fontWeight: '500'}, // R | I | D
|
||||||
{ tag: tags.unit, fontSize: '0.75rem' }, // indicies
|
{ tag: tags.unit, fontSize: '0.75rem' }, // indicies
|
||||||
]
|
]
|
||||||
}), [editable, colors, darkMode]);
|
}), [disabled, colors, darkMode]);
|
||||||
|
|
||||||
const editorExtensions = useMemo(
|
const editorExtensions = useMemo(
|
||||||
() => [
|
() => [
|
||||||
|
@ -139,7 +140,7 @@ function RSInput({
|
||||||
extensions={editorExtensions}
|
extensions={editorExtensions}
|
||||||
indentWithTab={false}
|
indentWithTab={false}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
editable={editable}
|
editable={!disabled}
|
||||||
onKeyDown={handleInput}
|
onKeyDown={handleInput}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -50,12 +50,13 @@ const editorSetup: BasicSetupOptions = {
|
||||||
|
|
||||||
interface RefsInputInputProps
|
interface RefsInputInputProps
|
||||||
extends Pick<ReactCodeMirrorProps,
|
extends Pick<ReactCodeMirrorProps,
|
||||||
'id'| 'editable' | 'height' | 'value' | 'className' | 'onFocus' | 'onBlur' | 'placeholder'
|
'id'| 'height' | 'value' | 'className' | 'onFocus' | 'onBlur' | 'placeholder'
|
||||||
> {
|
> {
|
||||||
label?: string
|
label?: string
|
||||||
innerref?: RefObject<ReactCodeMirrorRef> | undefined
|
innerref?: RefObject<ReactCodeMirrorRef> | undefined
|
||||||
onChange?: (newValue: string) => void
|
onChange?: (newValue: string) => void
|
||||||
items?: IConstituenta[]
|
items?: IConstituenta[]
|
||||||
|
disabled?: boolean
|
||||||
|
|
||||||
initialValue?: string
|
initialValue?: string
|
||||||
value?: string
|
value?: string
|
||||||
|
@ -63,7 +64,7 @@ extends Pick<ReactCodeMirrorProps,
|
||||||
}
|
}
|
||||||
|
|
||||||
function RefsInput({
|
function RefsInput({
|
||||||
id, label, innerref, editable, items,
|
id, label, innerref, disabled, items,
|
||||||
initialValue, value, resolved,
|
initialValue, value, resolved,
|
||||||
onFocus, onBlur, onChange,
|
onFocus, onBlur, onChange,
|
||||||
...props
|
...props
|
||||||
|
@ -89,13 +90,13 @@ function RefsInput({
|
||||||
return innerref ?? internalRef;
|
return innerref ?? internalRef;
|
||||||
}, [internalRef, innerref]);
|
}, [internalRef, innerref]);
|
||||||
|
|
||||||
const cursor = useMemo(() => editable ? 'cursor-text': 'cursor-default', [editable]);
|
const cursor = useMemo(() => !disabled ? 'cursor-text': 'cursor-default', [disabled]);
|
||||||
const customTheme: Extension = useMemo(
|
const customTheme: Extension = useMemo(
|
||||||
() => createTheme({
|
() => createTheme({
|
||||||
theme: darkMode ? 'dark' : 'light',
|
theme: darkMode ? 'dark' : 'light',
|
||||||
settings: {
|
settings: {
|
||||||
fontFamily: 'inherit',
|
fontFamily: 'inherit',
|
||||||
background: editable ? colors.bgInput : colors.bgDefault,
|
background: !disabled ? colors.bgInput : colors.bgDefault,
|
||||||
foreground: colors.fgDefault,
|
foreground: colors.fgDefault,
|
||||||
selection: colors.bgHover
|
selection: colors.bgHover
|
||||||
},
|
},
|
||||||
|
@ -104,7 +105,7 @@ function RefsInput({
|
||||||
{ tag: tags.literal, color: colors.fgTeal, cursor: 'default' }, // SyntacticReference
|
{ tag: tags.literal, color: colors.fgTeal, cursor: 'default' }, // SyntacticReference
|
||||||
{ tag: tags.comment, color: colors.fgRed }, // Error
|
{ tag: tags.comment, color: colors.fgRed }, // Error
|
||||||
]
|
]
|
||||||
}), [editable, colors, darkMode]);
|
}), [disabled, colors, darkMode]);
|
||||||
|
|
||||||
const editorExtensions = useMemo(
|
const editorExtensions = useMemo(
|
||||||
() => [
|
() => [
|
||||||
|
@ -216,7 +217,7 @@ function RefsInput({
|
||||||
|
|
||||||
indentWithTab={false}
|
indentWithTab={false}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
editable={editable}
|
editable={!disabled}
|
||||||
onKeyDown={handleInput}
|
onKeyDown={handleInput}
|
||||||
onFocus={handleFocusIn}
|
onFocus={handleFocusIn}
|
||||||
onBlur={handleFocusOut}
|
onBlur={handleFocusOut}
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
import { Dispatch } from 'react';
|
||||||
|
|
||||||
|
import ConceptSearch from '../../components/Common/ConceptSearch';
|
||||||
|
import RSInput from '../../components/RSInput';
|
||||||
|
import { IArgumentValue } from '../../models/rslang';
|
||||||
|
|
||||||
|
interface ArgumentsTabProps {
|
||||||
|
state: IArgumentsState
|
||||||
|
partialUpdate: Dispatch<Partial<IArgumentsState>>
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IArgumentsState {
|
||||||
|
arguments?: IArgumentValue[]
|
||||||
|
filterText: string
|
||||||
|
definition: string
|
||||||
|
}
|
||||||
|
|
||||||
|
function ArgumentsTab({state, partialUpdate}: ArgumentsTabProps) {
|
||||||
|
return (
|
||||||
|
<div className='flex flex-col gap-3'>
|
||||||
|
<ConceptSearch
|
||||||
|
value={state.filterText}
|
||||||
|
onChange={newValue => partialUpdate({ filterText: newValue} )}
|
||||||
|
dense
|
||||||
|
/>
|
||||||
|
<RSInput id='result'
|
||||||
|
placeholder='Итоговое определение'
|
||||||
|
height='4.8rem'
|
||||||
|
value={state.definition}
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
|
</div>);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ArgumentsTab;
|
|
@ -15,10 +15,10 @@ interface ConstituentaTabProps {
|
||||||
|
|
||||||
function ConstituentaTab({state, partialUpdate}: ConstituentaTabProps) {
|
function ConstituentaTab({state, partialUpdate}: ConstituentaTabProps) {
|
||||||
return (
|
return (
|
||||||
<div className='flex flex-col gap-2'>
|
<div className='flex flex-col gap-3'>
|
||||||
<div className='flex justify-center w-full gap-6'>
|
<div className='flex justify-center w-full gap-6'>
|
||||||
<SelectSingle
|
<SelectSingle
|
||||||
className='my-2 min-w-[15rem] self-center'
|
className='min-w-[15rem] self-center'
|
||||||
options={SelectorCstType}
|
options={SelectorCstType}
|
||||||
placeholder='Выберите тип'
|
placeholder='Выберите тип'
|
||||||
value={{ value: state.cst_type, label: labelCstType(state.cst_type) }}
|
value={{ value: state.cst_type, label: labelCstType(state.cst_type) }}
|
||||||
|
@ -40,7 +40,6 @@ function ConstituentaTab({state, partialUpdate}: ConstituentaTabProps) {
|
||||||
/>
|
/>
|
||||||
<RSInput id='expression' label='Формальное определение'
|
<RSInput id='expression' label='Формальное определение'
|
||||||
placeholder='Родоструктурное выражение, задающее формальное определение'
|
placeholder='Родоструктурное выражение, задающее формальное определение'
|
||||||
editable
|
|
||||||
height='4.8rem'
|
height='4.8rem'
|
||||||
value={state.definition_formal}
|
value={state.definition_formal}
|
||||||
onChange={value => partialUpdate({definition_formal: value})}
|
onChange={value => partialUpdate({definition_formal: value})}
|
|
@ -1,11 +1,10 @@
|
||||||
import { Dispatch, useEffect, useMemo, useState } from 'react';
|
import { Dispatch, useEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
|
import ConceptSearch from '../../components/Common/ConceptSearch';
|
||||||
import SelectSingle from '../../components/Common/SelectSingle';
|
import SelectSingle from '../../components/Common/SelectSingle';
|
||||||
import TextArea from '../../components/Common/TextArea';
|
import TextArea from '../../components/Common/TextArea';
|
||||||
import TextInput from '../../components/Common/TextInput';
|
|
||||||
import DataTable, { createColumnHelper,IConditionalStyle } from '../../components/DataTable';
|
import DataTable, { createColumnHelper,IConditionalStyle } from '../../components/DataTable';
|
||||||
import ConstituentaTooltip from '../../components/Help/ConstituentaTooltip';
|
import ConstituentaTooltip from '../../components/Help/ConstituentaTooltip';
|
||||||
import { MagnifyingGlassIcon } from '../../components/Icons';
|
|
||||||
import RSInput from '../../components/RSInput';
|
import RSInput from '../../components/RSInput';
|
||||||
import { useLibrary } from '../../context/LibraryContext';
|
import { useLibrary } from '../../context/LibraryContext';
|
||||||
import { useConceptTheme } from '../../context/ThemeContext';
|
import { useConceptTheme } from '../../context/ThemeContext';
|
||||||
|
@ -106,7 +105,6 @@ function TemplateTab({ state, partialUpdate }: TemplateTabProps) {
|
||||||
() => [
|
() => [
|
||||||
constituentaHelper.accessor('alias', {
|
constituentaHelper.accessor('alias', {
|
||||||
id: 'alias',
|
id: 'alias',
|
||||||
header: 'Имя',
|
|
||||||
size: 65,
|
size: 65,
|
||||||
minSize: 65,
|
minSize: 65,
|
||||||
cell: props => {
|
cell: props => {
|
||||||
|
@ -130,7 +128,6 @@ function TemplateTab({ state, partialUpdate }: TemplateTabProps) {
|
||||||
}),
|
}),
|
||||||
constituentaHelper.accessor('term_resolved', {
|
constituentaHelper.accessor('term_resolved', {
|
||||||
id: 'term',
|
id: 'term',
|
||||||
header: 'Термин',
|
|
||||||
size: 600,
|
size: 600,
|
||||||
minSize: 350,
|
minSize: 350,
|
||||||
maxSize: 600
|
maxSize: 600
|
||||||
|
@ -148,8 +145,8 @@ function TemplateTab({ state, partialUpdate }: TemplateTabProps) {
|
||||||
], [state.prototype, colors]);
|
], [state.prototype, colors]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='flex flex-col gap-2 mt-2'>
|
<div className='flex flex-col gap-3'>
|
||||||
<div className='flex justify-between gap-4'>
|
<div className='flex justify-between gap-3'>
|
||||||
<SelectSingle
|
<SelectSingle
|
||||||
className='w-full'
|
className='w-full'
|
||||||
options={categorySelector}
|
options={categorySelector}
|
||||||
|
@ -170,27 +167,19 @@ function TemplateTab({ state, partialUpdate }: TemplateTabProps) {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div className='relative'>
|
<ConceptSearch
|
||||||
<div className='absolute inset-y-0 flex items-center pl-3 pointer-events-none text-controls'>
|
value={state.filterText}
|
||||||
<MagnifyingGlassIcon />
|
onChange={newValue => partialUpdate({ filterText: newValue} )}
|
||||||
</div>
|
dense
|
||||||
<TextInput
|
/>
|
||||||
dimensions='w-full pl-10'
|
<div className='border min-h-[17.5rem] max-h-[17.5rem] text-sm overflow-y-auto select-none'>
|
||||||
placeholder='Поиск'
|
|
||||||
noOutline
|
|
||||||
value={state.filterText}
|
|
||||||
onChange={event => partialUpdate({ filterText: event.target.value} )}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='border min-h-[15.5rem] max-h-[15.5rem] text-sm overflow-y-auto select-none'>
|
|
||||||
<DataTable
|
<DataTable
|
||||||
data={filteredData}
|
data={filteredData}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
conditionalRowStyles={conditionalRowStyles}
|
conditionalRowStyles={conditionalRowStyles}
|
||||||
|
|
||||||
headPosition='0'
|
|
||||||
dense
|
dense
|
||||||
|
noHeader
|
||||||
|
|
||||||
noDataComponent={
|
noDataComponent={
|
||||||
<span className='flex flex-col justify-center p-2 text-center min-h-[5rem]'>
|
<span className='flex flex-col justify-center p-2 text-center min-h-[5rem]'>
|
||||||
|
@ -204,7 +193,7 @@ function TemplateTab({ state, partialUpdate }: TemplateTabProps) {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<TextArea id='term'
|
<TextArea id='term'
|
||||||
rows={2}
|
rows={1}
|
||||||
disabled
|
disabled
|
||||||
placeholder='Шаблон конституенты не выбран'
|
placeholder='Шаблон конституенты не выбран'
|
||||||
value={prototypeInfo}
|
value={prototypeInfo}
|
||||||
|
@ -213,7 +202,7 @@ function TemplateTab({ state, partialUpdate }: TemplateTabProps) {
|
||||||
<RSInput id='expression'
|
<RSInput id='expression'
|
||||||
height='4.8rem'
|
height='4.8rem'
|
||||||
placeholder='Выберите шаблон из списка'
|
placeholder='Выберите шаблон из списка'
|
||||||
editable={false}
|
disabled
|
||||||
value={state.prototype?.definition_formal}
|
value={state.prototype?.definition_formal}
|
||||||
/>
|
/>
|
||||||
</div>);
|
</div>);
|
156
rsconcept/frontend/src/dialogs/DlgConstituentaTemplate/index.tsx
Normal file
156
rsconcept/frontend/src/dialogs/DlgConstituentaTemplate/index.tsx
Normal file
|
@ -0,0 +1,156 @@
|
||||||
|
import { useEffect, useLayoutEffect, useState } from 'react';
|
||||||
|
import { TabList, TabPanel, Tabs } from 'react-tabs';
|
||||||
|
|
||||||
|
import ConceptTab from '../../components/Common/ConceptTab';
|
||||||
|
import ConceptTooltip from '../../components/Common/ConceptTooltip';
|
||||||
|
import Modal, { ModalProps } from '../../components/Common/Modal';
|
||||||
|
import HelpRSTemplates from '../../components/Help/HelpRSTemplates';
|
||||||
|
import { HelpIcon } from '../../components/Icons';
|
||||||
|
import usePartialUpdate from '../../hooks/usePartialUpdate';
|
||||||
|
import { CstType, ICstCreateData, IRSForm } from '../../models/rsform';
|
||||||
|
import { createAliasFor, validateCstAlias } from '../../utils/misc';
|
||||||
|
import ArgumentsTab, { IArgumentsState } from './ArgumentsTab';
|
||||||
|
import ConstituentaTab from './ConstituentaTab';
|
||||||
|
import TemplateTab, { ITemplateState } from './TemplateTab';
|
||||||
|
|
||||||
|
interface DlgConstituentaTemplateProps
|
||||||
|
extends Pick<ModalProps, 'hideWindow'> {
|
||||||
|
schema: IRSForm
|
||||||
|
onCreate: (data: ICstCreateData) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum TabID {
|
||||||
|
TEMPLATE = 0,
|
||||||
|
ARGUMENTS = 1,
|
||||||
|
CONSTITUENTA = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
function DlgConstituentaTemplate({ hideWindow, schema, onCreate }: DlgConstituentaTemplateProps) {
|
||||||
|
const [validated, setValidated] = useState(false);
|
||||||
|
const [ templateData, updateTemplateData ] = usePartialUpdate<ITemplateState>({
|
||||||
|
filterText: ''
|
||||||
|
});
|
||||||
|
const [ argumentsData, updateArgumentsData ] = usePartialUpdate<IArgumentsState>({
|
||||||
|
filterText: '',
|
||||||
|
definition: ''
|
||||||
|
});
|
||||||
|
const [cstData, updateCstData] = usePartialUpdate<ICstCreateData>({
|
||||||
|
cst_type: CstType.TERM,
|
||||||
|
insert_after: null,
|
||||||
|
alias: '',
|
||||||
|
convention: '',
|
||||||
|
definition_formal: '',
|
||||||
|
definition_raw: '',
|
||||||
|
term_raw: '',
|
||||||
|
term_forms: []
|
||||||
|
});
|
||||||
|
|
||||||
|
const [ activeTab, setActiveTab ] = useState(TabID.TEMPLATE);
|
||||||
|
|
||||||
|
const handleSubmit = () => onCreate(cstData);
|
||||||
|
|
||||||
|
useLayoutEffect(
|
||||||
|
() => {
|
||||||
|
updateCstData({ alias: createAliasFor(cstData.cst_type, schema) });
|
||||||
|
}, [cstData.cst_type, updateCstData, schema]);
|
||||||
|
|
||||||
|
useEffect(
|
||||||
|
() => {
|
||||||
|
setValidated(validateCstAlias(cstData.alias, cstData.cst_type, schema));
|
||||||
|
}, [cstData.alias, cstData.cst_type, schema]);
|
||||||
|
|
||||||
|
useLayoutEffect(
|
||||||
|
() => {
|
||||||
|
if (!templateData.prototype) {
|
||||||
|
updateCstData({
|
||||||
|
definition_raw: '',
|
||||||
|
definition_formal: '',
|
||||||
|
term_raw: ''
|
||||||
|
});
|
||||||
|
updateArgumentsData({
|
||||||
|
definition: '',
|
||||||
|
arguments: []
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
updateCstData({
|
||||||
|
cst_type: templateData.prototype.cst_type,
|
||||||
|
definition_raw: templateData.prototype.definition_raw,
|
||||||
|
definition_formal: templateData.prototype.definition_formal,
|
||||||
|
term_raw: templateData.prototype.term_raw
|
||||||
|
});
|
||||||
|
updateArgumentsData({
|
||||||
|
definition: templateData.prototype.definition_formal,
|
||||||
|
arguments: []
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [templateData.prototype, updateCstData, updateArgumentsData]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
title='Создание конституенты из шаблона'
|
||||||
|
hideWindow={hideWindow}
|
||||||
|
canSubmit={validated}
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
submitText='Создать'
|
||||||
|
>
|
||||||
|
<div className='max-w-[40rem] min-w-[40rem] min-h-[35rem] px-2 mb-1'>
|
||||||
|
<Tabs
|
||||||
|
selectedIndex={activeTab}
|
||||||
|
onSelect={setActiveTab}
|
||||||
|
defaultFocus
|
||||||
|
selectedTabClassName='clr-selected'
|
||||||
|
className='flex flex-col items-center'
|
||||||
|
>
|
||||||
|
<div className='flex gap-1 pl-6 mb-3'>
|
||||||
|
<TabList className='flex items-start font-semibold text-center border select-none clr-controls small-caps'>
|
||||||
|
<ConceptTab tooltip='Выбор шаблона выражения' className='border-r w-[8rem]'>
|
||||||
|
Шаблон
|
||||||
|
</ConceptTab>
|
||||||
|
<ConceptTab tooltip='Подстановка аргументов шаблона' className='border-r w-[8rem]'>
|
||||||
|
Аргументы
|
||||||
|
</ConceptTab>
|
||||||
|
<ConceptTab tooltip='Редактирование атрибутов конституенты' className='w-[8rem]'>
|
||||||
|
Конституента
|
||||||
|
</ConceptTab>
|
||||||
|
</TabList>
|
||||||
|
|
||||||
|
<div id='templates-help' className='px-1 py-1'>
|
||||||
|
<HelpIcon color='text-primary' size={5} />
|
||||||
|
</div>
|
||||||
|
<ConceptTooltip
|
||||||
|
anchorSelect='#templates-help'
|
||||||
|
className='max-w-[30rem] z-modal-tooltip'
|
||||||
|
offset={4}
|
||||||
|
>
|
||||||
|
<HelpRSTemplates />
|
||||||
|
</ConceptTooltip>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='w-full'>
|
||||||
|
<TabPanel>
|
||||||
|
<TemplateTab
|
||||||
|
state={templateData}
|
||||||
|
partialUpdate={updateTemplateData}
|
||||||
|
/>
|
||||||
|
</TabPanel>
|
||||||
|
|
||||||
|
<TabPanel>
|
||||||
|
<ArgumentsTab
|
||||||
|
state={argumentsData}
|
||||||
|
partialUpdate={updateArgumentsData}
|
||||||
|
/>
|
||||||
|
</TabPanel>
|
||||||
|
|
||||||
|
<TabPanel>
|
||||||
|
<ConstituentaTab
|
||||||
|
state={cstData}
|
||||||
|
partialUpdate={updateCstData}
|
||||||
|
/>
|
||||||
|
</TabPanel>
|
||||||
|
</div>
|
||||||
|
</Tabs>
|
||||||
|
</div>
|
||||||
|
</Modal>);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DlgConstituentaTemplate;
|
|
@ -79,7 +79,6 @@ function DlgCreateCst({ hideWindow, initial, schema, onCreate }: DlgCreateCstPro
|
||||||
/>
|
/>
|
||||||
<RSInput id='expression' label='Формальное определение'
|
<RSInput id='expression' label='Формальное определение'
|
||||||
placeholder='Родоструктурное выражение, задающее формальное определение'
|
placeholder='Родоструктурное выражение, задающее формальное определение'
|
||||||
editable
|
|
||||||
height='4.8rem'
|
height='4.8rem'
|
||||||
value={cstData.definition_formal}
|
value={cstData.definition_formal}
|
||||||
onChange={value => updateCstData({definition_formal: value})}
|
onChange={value => updateCstData({definition_formal: value})}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { createColumnHelper } from '@tanstack/react-table';
|
import { createColumnHelper } from '@tanstack/react-table';
|
||||||
import { useCallback, useEffect, useLayoutEffect, useMemo, useState } from 'react';
|
import { useCallback, useEffect, useLayoutEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
|
import ConceptSearch from '../../components/Common/ConceptSearch';
|
||||||
import ConceptTooltip from '../../components/Common/ConceptTooltip';
|
import ConceptTooltip from '../../components/Common/ConceptTooltip';
|
||||||
import Label from '../../components/Common/Label';
|
import Label from '../../components/Common/Label';
|
||||||
import Modal from '../../components/Common/Modal';
|
import Modal from '../../components/Common/Modal';
|
||||||
|
@ -9,7 +10,7 @@ import TextInput from '../../components/Common/TextInput';
|
||||||
import DataTable, { IConditionalStyle } from '../../components/DataTable';
|
import DataTable, { IConditionalStyle } from '../../components/DataTable';
|
||||||
import ConstituentaTooltip from '../../components/Help/ConstituentaTooltip';
|
import ConstituentaTooltip from '../../components/Help/ConstituentaTooltip';
|
||||||
import HelpTerminologyControl from '../../components/Help/HelpTerminologyControl';
|
import HelpTerminologyControl from '../../components/Help/HelpTerminologyControl';
|
||||||
import { HelpIcon, MagnifyingGlassIcon } from '../../components/Icons';
|
import { HelpIcon } from '../../components/Icons';
|
||||||
import { useConceptTheme } from '../../context/ThemeContext';
|
import { useConceptTheme } from '../../context/ThemeContext';
|
||||||
import {
|
import {
|
||||||
getCompatibleGrams, Grammeme,
|
getCompatibleGrams, Grammeme,
|
||||||
|
@ -183,7 +184,6 @@ function DlgEditReference({ hideWindow, items, initial, onSave }: DlgEditReferen
|
||||||
() => [
|
() => [
|
||||||
constituentaHelper.accessor('alias', {
|
constituentaHelper.accessor('alias', {
|
||||||
id: 'alias',
|
id: 'alias',
|
||||||
header: 'Имя',
|
|
||||||
size: 65,
|
size: 65,
|
||||||
minSize: 65,
|
minSize: 65,
|
||||||
cell: props => {
|
cell: props => {
|
||||||
|
@ -207,7 +207,6 @@ function DlgEditReference({ hideWindow, items, initial, onSave }: DlgEditReferen
|
||||||
}),
|
}),
|
||||||
constituentaHelper.accessor('term_resolved', {
|
constituentaHelper.accessor('term_resolved', {
|
||||||
id: 'term',
|
id: 'term',
|
||||||
header: 'Термин',
|
|
||||||
size: 600,
|
size: 600,
|
||||||
minSize: 350,
|
minSize: 350,
|
||||||
maxSize: 600
|
maxSize: 600
|
||||||
|
@ -287,35 +286,31 @@ function DlgEditReference({ hideWindow, items, initial, onSave }: DlgEditReferen
|
||||||
</div>}
|
</div>}
|
||||||
{type === ReferenceType.ENTITY &&
|
{type === ReferenceType.ENTITY &&
|
||||||
<div className='flex flex-col gap-2'>
|
<div className='flex flex-col gap-2'>
|
||||||
<div className='relative'>
|
<div>
|
||||||
<div className='absolute inset-y-0 flex items-center pl-3 pointer-events-none text-controls'>
|
<ConceptSearch
|
||||||
<MagnifyingGlassIcon />
|
|
||||||
</div>
|
|
||||||
<TextInput
|
|
||||||
dimensions='w-full pl-10'
|
|
||||||
placeholder='Поиск'
|
|
||||||
value={filter}
|
value={filter}
|
||||||
onChange={event => setFilter(event.target.value)}
|
onChange={newValue => setFilter(newValue)}
|
||||||
|
dense
|
||||||
|
/>
|
||||||
|
<div className='border min-h-[15.5rem] max-h-[15.5rem] text-sm overflow-y-auto'>
|
||||||
|
<DataTable
|
||||||
|
data={filteredData}
|
||||||
|
columns={columnsConstituenta}
|
||||||
|
conditionalRowStyles={conditionalRowStyles}
|
||||||
|
|
||||||
|
noHeader
|
||||||
|
dense
|
||||||
|
|
||||||
|
noDataComponent={
|
||||||
|
<span className='flex flex-col justify-center p-2 text-center min-h-[5rem]'>
|
||||||
|
<p>Список конституент пуст</p>
|
||||||
|
<p>Измените параметры фильтра</p>
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
|
||||||
|
onRowClicked={handleSelectConstituenta}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className='border min-h-[15.5rem] max-h-[15.5rem] text-sm overflow-y-auto'>
|
|
||||||
<DataTable
|
|
||||||
data={filteredData}
|
|
||||||
columns={columnsConstituenta}
|
|
||||||
conditionalRowStyles={conditionalRowStyles}
|
|
||||||
|
|
||||||
headPosition='0'
|
|
||||||
dense
|
|
||||||
|
|
||||||
noDataComponent={
|
|
||||||
<span className='flex flex-col justify-center p-2 text-center min-h-[5rem]'>
|
|
||||||
<p>Список конституент пуст</p>
|
|
||||||
<p>Измените параметры фильтра</p>
|
|
||||||
</span>
|
|
||||||
}
|
|
||||||
|
|
||||||
onRowClicked={handleSelectConstituenta}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div className='flex gap-4 flex-start'>
|
<div className='flex gap-4 flex-start'>
|
||||||
<TextInput
|
<TextInput
|
||||||
|
|
|
@ -217,7 +217,7 @@ function DlgEditWordForms({ hideWindow, target, onSave }: DlgEditWordFormsProps)
|
||||||
>
|
>
|
||||||
<div className='relative w-full'>
|
<div className='relative w-full'>
|
||||||
<div className='absolute top-0 right-0'>
|
<div className='absolute top-0 right-0'>
|
||||||
<div id='terminology-help' className='px-1 py-1'>
|
<div id='terminology-help' className='px-1 py-1'>
|
||||||
<HelpIcon color='text-primary' size={5} />
|
<HelpIcon color='text-primary' size={5} />
|
||||||
</div>
|
</div>
|
||||||
<ConceptTooltip
|
<ConceptTooltip
|
||||||
|
|
|
@ -1,121 +0,0 @@
|
||||||
import { useEffect, useLayoutEffect, useState } from 'react';
|
|
||||||
|
|
||||||
import ConceptTooltip from '../../components/Common/ConceptTooltip';
|
|
||||||
import Modal, { ModalProps } from '../../components/Common/Modal';
|
|
||||||
import SwitchButton from '../../components/Common/SwitchButton';
|
|
||||||
import HelpRSTemplates from '../../components/Help/HelpRSTemplates';
|
|
||||||
import { HelpIcon } from '../../components/Icons';
|
|
||||||
import usePartialUpdate from '../../hooks/usePartialUpdate';
|
|
||||||
import { CstType, ICstCreateData, IRSForm } from '../../models/rsform';
|
|
||||||
import { createAliasFor, validateCstAlias } from '../../utils/misc';
|
|
||||||
import ConstituentaTab from './ConstituentaTab';
|
|
||||||
import TemplateTab, { ITemplateState } from './TemplateTab';
|
|
||||||
|
|
||||||
interface DlgTemplatesProps
|
|
||||||
extends Pick<ModalProps, 'hideWindow'> {
|
|
||||||
schema: IRSForm
|
|
||||||
onCreate: (data: ICstCreateData) => void
|
|
||||||
}
|
|
||||||
|
|
||||||
function DlgTemplates({ hideWindow, schema, onCreate }: DlgTemplatesProps) {
|
|
||||||
const [validated, setValidated] = useState(false);
|
|
||||||
const [cstData, updateCstData] = usePartialUpdate<ICstCreateData>({
|
|
||||||
cst_type: CstType.TERM,
|
|
||||||
insert_after: null,
|
|
||||||
alias: '',
|
|
||||||
convention: '',
|
|
||||||
definition_formal: '',
|
|
||||||
definition_raw: '',
|
|
||||||
term_raw: '',
|
|
||||||
term_forms: []
|
|
||||||
});
|
|
||||||
const [ templateData, updateTemplateData ] = usePartialUpdate<ITemplateState>({
|
|
||||||
filterText: ''
|
|
||||||
});
|
|
||||||
|
|
||||||
const [ showAttributes, setShowAttributes ] = useState(false);
|
|
||||||
|
|
||||||
const handleSubmit = () => onCreate(cstData);
|
|
||||||
|
|
||||||
useLayoutEffect(
|
|
||||||
() => {
|
|
||||||
updateCstData({ alias: createAliasFor(cstData.cst_type, schema) });
|
|
||||||
}, [cstData.cst_type, updateCstData, schema]);
|
|
||||||
|
|
||||||
useEffect(
|
|
||||||
() => {
|
|
||||||
setValidated(validateCstAlias(cstData.alias, cstData.cst_type, schema));
|
|
||||||
}, [cstData.alias, cstData.cst_type, schema]);
|
|
||||||
|
|
||||||
useLayoutEffect(
|
|
||||||
() => {
|
|
||||||
if (!templateData.prototype) {
|
|
||||||
updateCstData({
|
|
||||||
definition_raw: '',
|
|
||||||
definition_formal: '',
|
|
||||||
term_raw: ''
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
updateCstData({
|
|
||||||
cst_type: templateData.prototype.cst_type,
|
|
||||||
definition_raw: templateData.prototype.definition_raw,
|
|
||||||
definition_formal: templateData.prototype.definition_formal,
|
|
||||||
term_raw: templateData.prototype.term_raw
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, [templateData.prototype, updateCstData]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
title='Создание конституенты из шаблона'
|
|
||||||
hideWindow={hideWindow}
|
|
||||||
canSubmit={validated}
|
|
||||||
onSubmit={handleSubmit}
|
|
||||||
submitText='Создать'
|
|
||||||
>
|
|
||||||
<div className='h-fit max-w-[40rem] min-w-[40rem] min-h-[35rem] px-2 mb-2 flex flex-col justify-stretch gap-3'>
|
|
||||||
<div className='flex items-center self-center flex-start'>
|
|
||||||
<SwitchButton
|
|
||||||
label='Шаблон'
|
|
||||||
tooltip='Выбор шаблона выражения'
|
|
||||||
dimensions='min-w-[10rem] h-fit'
|
|
||||||
value={false}
|
|
||||||
isSelected={!showAttributes}
|
|
||||||
onSelect={(value) => setShowAttributes(value)}
|
|
||||||
/>
|
|
||||||
<SwitchButton
|
|
||||||
label='Конституента'
|
|
||||||
tooltip='Редактирование атрибутов конституенты'
|
|
||||||
dimensions='min-w-[10rem] h-fit'
|
|
||||||
value={true}
|
|
||||||
isSelected={showAttributes}
|
|
||||||
onSelect={(value) => setShowAttributes(value)}
|
|
||||||
/>
|
|
||||||
<div id='templates-help' className='px-1 py-1'>
|
|
||||||
<HelpIcon color='text-primary' size={5} />
|
|
||||||
</div>
|
|
||||||
<ConceptTooltip
|
|
||||||
anchorSelect='#templates-help'
|
|
||||||
className='max-w-[30rem] z-modal-tooltip'
|
|
||||||
offset={4}
|
|
||||||
>
|
|
||||||
<HelpRSTemplates />
|
|
||||||
</ConceptTooltip>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{ !showAttributes &&
|
|
||||||
<TemplateTab
|
|
||||||
state={templateData}
|
|
||||||
partialUpdate={updateTemplateData}
|
|
||||||
/>}
|
|
||||||
|
|
||||||
{ showAttributes &&
|
|
||||||
<ConstituentaTab
|
|
||||||
state={cstData}
|
|
||||||
partialUpdate={updateCstData}
|
|
||||||
/>}
|
|
||||||
</div>
|
|
||||||
</Modal>);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default DlgTemplates;
|
|
|
@ -2,14 +2,14 @@ import { useCallback, useState } from 'react'
|
||||||
|
|
||||||
import { type ErrorInfo } from '../components/BackendError';
|
import { type ErrorInfo } from '../components/BackendError';
|
||||||
import { CstType, IConstituenta, type IRSForm } from '../models/rsform';
|
import { CstType, IConstituenta, type IRSForm } from '../models/rsform';
|
||||||
import { IExpressionParse, IFunctionArg } from '../models/rslang';
|
import { IExpressionParse, IArgumentInfo } from '../models/rslang';
|
||||||
import { RSErrorType } from '../models/rslang';
|
import { RSErrorType } from '../models/rslang';
|
||||||
import { DataCallback, postCheckExpression } from '../utils/backendAPI';
|
import { DataCallback, postCheckExpression } from '../utils/backendAPI';
|
||||||
import { getCstExpressionPrefix } from '../utils/misc';
|
import { getCstExpressionPrefix } from '../utils/misc';
|
||||||
|
|
||||||
const LOGIC_TYPIIFCATION = 'LOGIC';
|
const LOGIC_TYPIIFCATION = 'LOGIC';
|
||||||
|
|
||||||
function checkTypeConsistency(type: CstType, typification: string, args: IFunctionArg[]): boolean {
|
function checkTypeConsistency(type: CstType, typification: string, args: IArgumentInfo[]): boolean {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case CstType.BASE:
|
case CstType.BASE:
|
||||||
case CstType.CONSTANT:
|
case CstType.CONSTANT:
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { TextMatcher } from '../utils/utils'
|
||||||
import { ILibraryUpdateData } from './library'
|
import { ILibraryUpdateData } from './library'
|
||||||
import { ILibraryItem } from './library'
|
import { ILibraryItem } from './library'
|
||||||
import { CstMatchMode } from './miscelanious'
|
import { CstMatchMode } from './miscelanious'
|
||||||
import { IFunctionArg, ParsingStatus, ValueClass } from './rslang'
|
import { IArgumentInfo, ParsingStatus, ValueClass } from './rslang'
|
||||||
|
|
||||||
export enum CstType {
|
export enum CstType {
|
||||||
BASE = 'basic',
|
BASE = 'basic',
|
||||||
|
@ -67,7 +67,7 @@ extends IConstituentaMeta {
|
||||||
valueClass: ValueClass
|
valueClass: ValueClass
|
||||||
typification: string
|
typification: string
|
||||||
syntaxTree: string
|
syntaxTree: string
|
||||||
args: IFunctionArg[]
|
args: IArgumentInfo[]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,11 +43,16 @@ export interface ISyntaxTreeNode {
|
||||||
}
|
}
|
||||||
export type SyntaxTree = ISyntaxTreeNode[]
|
export type SyntaxTree = ISyntaxTreeNode[]
|
||||||
|
|
||||||
export interface IFunctionArg {
|
export interface IArgumentInfo {
|
||||||
alias: string
|
alias: string
|
||||||
typification: string
|
typification: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IArgumentValue {
|
||||||
|
alias: string
|
||||||
|
value?: string
|
||||||
|
}
|
||||||
|
|
||||||
export interface IExpressionParse {
|
export interface IExpressionParse {
|
||||||
parseResult: boolean
|
parseResult: boolean
|
||||||
syntax: Syntax
|
syntax: Syntax
|
||||||
|
@ -56,7 +61,7 @@ export interface IExpressionParse {
|
||||||
errors: IRSErrorDescription[]
|
errors: IRSErrorDescription[]
|
||||||
astText: string
|
astText: string
|
||||||
ast: SyntaxTree
|
ast: SyntaxTree
|
||||||
args: IFunctionArg[]
|
args: IArgumentInfo[]
|
||||||
}
|
}
|
||||||
|
|
||||||
//! RS language token types enumeration
|
//! RS language token types enumeration
|
||||||
|
|
|
@ -235,7 +235,7 @@ function EditorConstituenta({
|
||||||
value={term}
|
value={term}
|
||||||
initialValue={activeCst?.term_raw ?? ''}
|
initialValue={activeCst?.term_raw ?? ''}
|
||||||
resolved={activeCst?.term_resolved ?? ''}
|
resolved={activeCst?.term_resolved ?? ''}
|
||||||
editable={isEnabled}
|
disabled={!isEnabled}
|
||||||
onChange={newValue => setTerm(newValue)}
|
onChange={newValue => setTerm(newValue)}
|
||||||
/>
|
/>
|
||||||
<TextArea id='typification' label='Типизация'
|
<TextArea id='typification' label='Типизация'
|
||||||
|
@ -262,7 +262,7 @@ function EditorConstituenta({
|
||||||
value={textDefinition}
|
value={textDefinition}
|
||||||
initialValue={activeCst?.definition_raw ?? ''}
|
initialValue={activeCst?.definition_raw ?? ''}
|
||||||
resolved={activeCst?.definition_resolved ?? ''}
|
resolved={activeCst?.definition_resolved ?? ''}
|
||||||
editable={isEnabled}
|
disabled={!isEnabled}
|
||||||
onChange={newValue => setTextDefinition(newValue)}
|
onChange={newValue => setTextDefinition(newValue)}
|
||||||
/>
|
/>
|
||||||
<TextArea id='convention' label='Конвенция / Комментарий'
|
<TextArea id='convention' label='Конвенция / Комментарий'
|
||||||
|
|
|
@ -131,7 +131,7 @@ function EditorRSExpression({
|
||||||
<RSInput innerref={rsInput}
|
<RSInput innerref={rsInput}
|
||||||
height='4.8rem'
|
height='4.8rem'
|
||||||
value={value}
|
value={value}
|
||||||
editable={!disabled}
|
disabled={disabled}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -14,12 +14,12 @@ import { useConceptNavigation } from '../../context/NagivationContext';
|
||||||
import { useRSForm } from '../../context/RSFormContext';
|
import { useRSForm } from '../../context/RSFormContext';
|
||||||
import { useConceptTheme } from '../../context/ThemeContext';
|
import { useConceptTheme } from '../../context/ThemeContext';
|
||||||
import DlgCloneRSForm from '../../dialogs/DlgCloneRSForm';
|
import DlgCloneRSForm from '../../dialogs/DlgCloneRSForm';
|
||||||
|
import DlgConstituentaTemplate from '../../dialogs/DlgConstituentaTemplate';
|
||||||
import DlgCreateCst from '../../dialogs/DlgCreateCst';
|
import DlgCreateCst from '../../dialogs/DlgCreateCst';
|
||||||
import DlgDeleteCst from '../../dialogs/DlgDeleteCst';
|
import DlgDeleteCst from '../../dialogs/DlgDeleteCst';
|
||||||
import DlgEditWordForms from '../../dialogs/DlgEditWordForms';
|
import DlgEditWordForms from '../../dialogs/DlgEditWordForms';
|
||||||
import DlgRenameCst from '../../dialogs/DlgRenameCst';
|
import DlgRenameCst from '../../dialogs/DlgRenameCst';
|
||||||
import DlgShowAST from '../../dialogs/DlgShowAST';
|
import DlgShowAST from '../../dialogs/DlgShowAST';
|
||||||
import DlgTemplates from '../../dialogs/DlgTemplates';
|
|
||||||
import DlgUploadRSForm from '../../dialogs/DlgUploadRSForm';
|
import DlgUploadRSForm from '../../dialogs/DlgUploadRSForm';
|
||||||
import useModificationPrompt from '../../hooks/useModificationPrompt';
|
import useModificationPrompt from '../../hooks/useModificationPrompt';
|
||||||
import { ICstCreateData, ICstRenameData, ICstUpdateData, TermForm } from '../../models/rsform';
|
import { ICstCreateData, ICstRenameData, ICstUpdateData, TermForm } from '../../models/rsform';
|
||||||
|
@ -64,7 +64,7 @@ function RSTabs() {
|
||||||
|
|
||||||
const { isModified, setIsModified } = useModificationPrompt();
|
const { isModified, setIsModified } = useModificationPrompt();
|
||||||
|
|
||||||
const [activeTab, setActiveTab] = useState<RSTabID>(RSTabID.CARD);
|
const [activeTab, setActiveTab] = useState(RSTabID.CARD);
|
||||||
const [activeID, setActiveID] = useState<number | undefined>(undefined);
|
const [activeID, setActiveID] = useState<number | undefined>(undefined);
|
||||||
const activeCst = useMemo(
|
const activeCst = useMemo(
|
||||||
() => schema?.items?.find(cst => cst.id === activeID)
|
() => schema?.items?.find(cst => cst.id === activeID)
|
||||||
|
@ -375,7 +375,7 @@ function RSTabs() {
|
||||||
target={activeCst!}
|
target={activeCst!}
|
||||||
/>}
|
/>}
|
||||||
{showTemplates &&
|
{showTemplates &&
|
||||||
<DlgTemplates
|
<DlgConstituentaTemplate
|
||||||
schema={schema}
|
schema={schema}
|
||||||
hideWindow={() => setShowTemplates(false)}
|
hideWindow={() => setShowTemplates(false)}
|
||||||
onCreate={handleCreateCst}
|
onCreate={handleCreateCst}
|
||||||
|
@ -383,7 +383,7 @@ function RSTabs() {
|
||||||
<Tabs
|
<Tabs
|
||||||
selectedIndex={activeTab}
|
selectedIndex={activeTab}
|
||||||
onSelect={onSelectTab}
|
onSelect={onSelectTab}
|
||||||
defaultFocus={true}
|
defaultFocus
|
||||||
selectedTabClassName='clr-selected'
|
selectedTabClassName='clr-selected'
|
||||||
className='flex flex-col items-center w-full'
|
className='flex flex-col items-center w-full'
|
||||||
>
|
>
|
||||||
|
@ -399,21 +399,21 @@ function RSTabs() {
|
||||||
showUploadDialog={() => setShowUpload(true)}
|
showUploadDialog={() => setShowUpload(true)}
|
||||||
/>
|
/>
|
||||||
<ConceptTab
|
<ConceptTab
|
||||||
className='border-x-2 min-w-[7.8rem]'
|
className='border-x-2'
|
||||||
title={`Название схемы: ${schema.title ?? ''}`}
|
tooltip={`Название схемы: ${schema.title ?? ''}`}
|
||||||
>
|
>
|
||||||
Паспорт схемы
|
Паспорт схемы
|
||||||
</ConceptTab>
|
</ConceptTab>
|
||||||
<ConceptTab
|
<ConceptTab
|
||||||
className='flex justify-between gap-2 border-r-2 w-fit'
|
className='border-r-2'
|
||||||
title={`Всего конституент: ${schema.stats?.count_all ?? 0}\nКоличество ошибок: ${schema.stats?.count_errors ?? 0}`}
|
tooltip={`Всего конституент: ${schema.stats?.count_all ?? 0}\nКоличество ошибок: ${schema.stats?.count_errors ?? 0}`}
|
||||||
>
|
>
|
||||||
<span>Конституенты</span>
|
Конституенты
|
||||||
</ConceptTab>
|
</ConceptTab>
|
||||||
<ConceptTab className='border-r-2 min-w-[5.2rem]'>
|
<ConceptTab className='border-r-2'>
|
||||||
Редактор
|
Редактор
|
||||||
</ConceptTab>
|
</ConceptTab>
|
||||||
<ConceptTab className='min-w-[6.5rem]'>
|
<ConceptTab className=''>
|
||||||
Граф термов
|
Граф термов
|
||||||
</ConceptTab>
|
</ConceptTab>
|
||||||
</TabList>
|
</TabList>
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
import { GramData,Grammeme, ReferenceType } from '../models/language';
|
import { GramData,Grammeme, ReferenceType } from '../models/language';
|
||||||
import { CstMatchMode, DependencyMode, HelpTopic, LibraryFilterStrategy } from '../models/miscelanious';
|
import { CstMatchMode, DependencyMode, HelpTopic, LibraryFilterStrategy } from '../models/miscelanious';
|
||||||
import { CstClass, CstType, ExpressionStatus, IConstituenta } from '../models/rsform';
|
import { CstClass, CstType, ExpressionStatus, IConstituenta } from '../models/rsform';
|
||||||
import { IFunctionArg, IRSErrorDescription, ISyntaxTreeNode, ParsingStatus, RSErrorType, TokenID } from '../models/rslang';
|
import { IArgumentInfo, IRSErrorDescription, ISyntaxTreeNode, ParsingStatus, RSErrorType, TokenID } from '../models/rslang';
|
||||||
|
|
||||||
export function describeConstituenta(cst: IConstituenta): string {
|
export function describeConstituenta(cst: IConstituenta): string {
|
||||||
if (cst.cst_type === CstType.STRUCTURED) {
|
if (cst.cst_type === CstType.STRUCTURED) {
|
||||||
|
@ -309,7 +309,7 @@ export function describeCstClass(cclass: CstClass): string {
|
||||||
export function labelTypification({ isValid, resultType, args }: {
|
export function labelTypification({ isValid, resultType, args }: {
|
||||||
isValid: boolean;
|
isValid: boolean;
|
||||||
resultType: string;
|
resultType: string;
|
||||||
args: IFunctionArg[];
|
args: IArgumentInfo[];
|
||||||
}): string {
|
}): string {
|
||||||
if (!isValid) {
|
if (!isValid) {
|
||||||
return 'N/A';
|
return 'N/A';
|
||||||
|
|
|
@ -4,18 +4,6 @@ export function assertIsNode(e: EventTarget | null): asserts e is Node {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function delay(ms: number) {
|
|
||||||
return await new Promise(resolve => setTimeout(resolve, ms));
|
|
||||||
}
|
|
||||||
|
|
||||||
export function trimString(target: string, maxLen: number): string {
|
|
||||||
if (target.length < maxLen) {
|
|
||||||
return target;
|
|
||||||
} else {
|
|
||||||
return target.substring(0, maxLen) + '...';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapper class for generalized text matching.
|
* Wrapper class for generalized text matching.
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in New Issue
Block a user