F: Rework ChangeInputSchema dialog
This commit is contained in:
parent
9f84154237
commit
8a4e3a6d15
|
@ -1,4 +1,5 @@
|
||||||
import { queryOptions } from '@tanstack/react-query';
|
import { queryOptions } from '@tanstack/react-query';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
import { axiosDelete, axiosGet, axiosPatch, axiosPost } from '@/backend/apiTransport';
|
import { axiosDelete, axiosGet, axiosPatch, axiosPost } from '@/backend/apiTransport';
|
||||||
import { DELAYS } from '@/backend/configuration';
|
import { DELAYS } from '@/backend/configuration';
|
||||||
|
@ -8,7 +9,6 @@ import {
|
||||||
ICstSubstitute,
|
ICstSubstitute,
|
||||||
ICstSubstituteEx,
|
ICstSubstituteEx,
|
||||||
IOperation,
|
IOperation,
|
||||||
IOperationPosition,
|
|
||||||
OperationID,
|
OperationID,
|
||||||
OperationType
|
OperationType
|
||||||
} from '@/features/oss/models/oss';
|
} from '@/features/oss/models/oss';
|
||||||
|
@ -79,12 +79,33 @@ export interface IInputCreatedResponse {
|
||||||
oss: IOperationSchemaDTO;
|
oss: IOperationSchemaDTO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents {@link IOperation} position.
|
||||||
|
*/
|
||||||
|
export const OperationPositionSchema = z.object({
|
||||||
|
id: z.number(),
|
||||||
|
position_x: z.number(),
|
||||||
|
position_y: z.number()
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents {@link IOperation} position.
|
||||||
|
*/
|
||||||
|
export type IOperationPosition = z.infer<typeof OperationPositionSchema>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents {@link IOperation} data, used in setInput process.
|
* Represents {@link IOperation} data, used in setInput process.
|
||||||
*/
|
*/
|
||||||
export interface IInputUpdateDTO extends ITargetOperation {
|
export const InputUpdateSchema = z.object({
|
||||||
input: LibraryItemID | null;
|
target: z.number(),
|
||||||
}
|
positions: z.array(OperationPositionSchema),
|
||||||
|
input: z.number().nullable()
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents {@link IOperation} data, used in setInput process.
|
||||||
|
*/
|
||||||
|
export type IInputUpdateDTO = z.infer<typeof InputUpdateSchema>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents {@link IOperation} data, used in update process.
|
* Represents {@link IOperation} data, used in update process.
|
||||||
|
|
|
@ -2,9 +2,8 @@ import { useMutation } from '@tanstack/react-query';
|
||||||
|
|
||||||
import { useUpdateTimestamp } from '@/features/library/backend/useUpdateTimestamp';
|
import { useUpdateTimestamp } from '@/features/library/backend/useUpdateTimestamp';
|
||||||
import { LibraryItemID } from '@/features/library/models/library';
|
import { LibraryItemID } from '@/features/library/models/library';
|
||||||
import { IOperationPosition } from '@/features/oss/models/oss';
|
|
||||||
|
|
||||||
import { ossApi } from './api';
|
import { IOperationPosition, ossApi } from './api';
|
||||||
|
|
||||||
export const useUpdatePositions = () => {
|
export const useUpdatePositions = () => {
|
||||||
const { updateTimestamp } = useUpdateTimestamp();
|
const { updateTimestamp } = useUpdateTimestamp();
|
||||||
|
|
|
@ -1,44 +1,51 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { useState } from 'react';
|
import { Controller, useForm } from 'react-hook-form';
|
||||||
|
|
||||||
import { MiniButton } from '@/components/Control';
|
import { MiniButton } from '@/components/Control';
|
||||||
import { IconReset } from '@/components/Icons';
|
import { IconReset } from '@/components/Icons';
|
||||||
import { Label } from '@/components/Input';
|
import { Label } from '@/components/Input';
|
||||||
import { ModalForm } from '@/components/Modal';
|
import { ModalForm } from '@/components/Modal';
|
||||||
import { useLibrary } from '@/features/library/backend/useLibrary';
|
import { useLibrary } from '@/features/library/backend/useLibrary';
|
||||||
import { ILibraryItem, LibraryItemID, LibraryItemType } from '@/features/library/models/library';
|
import { ILibraryItem, LibraryItemType } from '@/features/library/models/library';
|
||||||
import PickSchema from '@/features/rsform/components/PickSchema';
|
import PickSchema from '@/features/rsform/components/PickSchema';
|
||||||
import { useDialogsStore } from '@/stores/dialogs';
|
import { useDialogsStore } from '@/stores/dialogs';
|
||||||
|
|
||||||
import { IOperation, IOperationSchema, OperationID } from '../models/oss';
|
import { IInputUpdateDTO, InputUpdateSchema, IOperationPosition } from '../backend/api';
|
||||||
|
import { useInputUpdate } from '../backend/useInputUpdate';
|
||||||
|
import { IOperation, IOperationSchema } from '../models/oss';
|
||||||
import { sortItemsForOSS } from '../models/ossAPI';
|
import { sortItemsForOSS } from '../models/ossAPI';
|
||||||
|
|
||||||
export interface DlgChangeInputSchemaProps {
|
export interface DlgChangeInputSchemaProps {
|
||||||
oss: IOperationSchema;
|
oss: IOperationSchema;
|
||||||
target: IOperation;
|
target: IOperation;
|
||||||
onSubmit: (target: OperationID, newSchema: LibraryItemID | undefined) => void;
|
positions: IOperationPosition[];
|
||||||
}
|
}
|
||||||
|
|
||||||
function DlgChangeInputSchema() {
|
function DlgChangeInputSchema() {
|
||||||
const { oss, target, onSubmit } = useDialogsStore(state => state.props as DlgChangeInputSchemaProps);
|
const { oss, target, positions } = useDialogsStore(state => state.props as DlgChangeInputSchemaProps);
|
||||||
const [selected, setSelected] = useState<LibraryItemID | undefined>(target.result ?? undefined);
|
const { inputUpdate } = useInputUpdate();
|
||||||
|
|
||||||
|
const { setValue, handleSubmit, control } = useForm<IInputUpdateDTO>({
|
||||||
|
resolver: zodResolver(InputUpdateSchema),
|
||||||
|
defaultValues: {
|
||||||
|
target: target.id,
|
||||||
|
positions: positions,
|
||||||
|
input: target.result
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const { items } = useLibrary();
|
const { items } = useLibrary();
|
||||||
const sortedItems = sortItemsForOSS(oss, items);
|
const sortedItems = sortItemsForOSS(oss, items);
|
||||||
const isValid = target.result !== selected;
|
|
||||||
|
|
||||||
function baseFilter(item: ILibraryItem) {
|
function baseFilter(item: ILibraryItem) {
|
||||||
return !oss.schemas.includes(item.id) || item.id === selected || item.id === target.result;
|
return !oss.schemas.includes(item.id) || item.id === target.result;
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSelectLocation(newValue: LibraryItemID) {
|
function onSubmit(data: IInputUpdateDTO) {
|
||||||
setSelected(newValue);
|
inputUpdate({ itemID: oss.id, data: data });
|
||||||
}
|
|
||||||
|
|
||||||
function handleSubmit() {
|
|
||||||
onSubmit(target.id, selected);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -46,8 +53,7 @@ function DlgChangeInputSchema() {
|
||||||
overflowVisible
|
overflowVisible
|
||||||
header='Выбор концептуальной схемы'
|
header='Выбор концептуальной схемы'
|
||||||
submitText='Подтвердить выбор'
|
submitText='Подтвердить выбор'
|
||||||
canSubmit={isValid}
|
onSubmit={event => void handleSubmit(onSubmit)(event)}
|
||||||
onSubmit={handleSubmit}
|
|
||||||
className={clsx('w-[35rem]', 'pb-3 px-6 cc-column')}
|
className={clsx('w-[35rem]', 'pb-3 px-6 cc-column')}
|
||||||
>
|
>
|
||||||
<div className='flex justify-between gap-3 items-center'>
|
<div className='flex justify-between gap-3 items-center'>
|
||||||
|
@ -58,18 +64,23 @@ function DlgChangeInputSchema() {
|
||||||
noHover
|
noHover
|
||||||
noPadding
|
noPadding
|
||||||
icon={<IconReset size='1.25rem' className='icon-primary' />}
|
icon={<IconReset size='1.25rem' className='icon-primary' />}
|
||||||
onClick={() => setSelected(undefined)}
|
onClick={() => setValue('input', null)}
|
||||||
disabled={selected == undefined}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<PickSchema
|
<Controller
|
||||||
items={sortedItems}
|
name='input'
|
||||||
itemType={LibraryItemType.RSFORM}
|
control={control}
|
||||||
value={selected} // prettier: split-line
|
render={({ field }) => (
|
||||||
onChange={handleSelectLocation}
|
<PickSchema
|
||||||
rows={14}
|
items={sortedItems}
|
||||||
baseFilter={baseFilter}
|
itemType={LibraryItemType.RSFORM}
|
||||||
|
value={field.value}
|
||||||
|
onChange={field.onChange}
|
||||||
|
rows={14}
|
||||||
|
baseFilter={baseFilter}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
</ModalForm>
|
</ModalForm>
|
||||||
);
|
);
|
||||||
|
|
|
@ -43,18 +43,6 @@ export interface IOperation {
|
||||||
arguments: OperationID[];
|
arguments: OperationID[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents {@link IOperation} position.
|
|
||||||
*/
|
|
||||||
export interface IOperationPosition extends Pick<IOperation, 'id' | 'position_x' | 'position_y'> {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents all {@link IOperation} positions in {@link IOperationSchema}.
|
|
||||||
*/
|
|
||||||
export interface IPositions {
|
|
||||||
positions: IOperationPosition[];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents {@link IOperation} Argument.
|
* Represents {@link IOperation} Argument.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -16,15 +16,8 @@ import { describeSubstitutionError, information } from '@/utils/labels';
|
||||||
import { TextMatcher } from '@/utils/utils';
|
import { TextMatcher } from '@/utils/utils';
|
||||||
|
|
||||||
import { Graph } from '../../../models/Graph';
|
import { Graph } from '../../../models/Graph';
|
||||||
import {
|
import { IOperationPosition } from '../backend/api';
|
||||||
ICstSubstitute,
|
import { ICstSubstitute, IOperation, IOperationSchema, OperationID, OperationType, SubstitutionErrorType } from './oss';
|
||||||
IOperation,
|
|
||||||
IOperationPosition,
|
|
||||||
IOperationSchema,
|
|
||||||
OperationID,
|
|
||||||
OperationType,
|
|
||||||
SubstitutionErrorType
|
|
||||||
} from './oss';
|
|
||||||
import { Position2D } from './ossLayout';
|
import { Position2D } from './ossLayout';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -15,14 +15,14 @@ import { useRoleStore } from '@/stores/role';
|
||||||
import { PARAMETER } from '@/utils/constants';
|
import { PARAMETER } from '@/utils/constants';
|
||||||
import { prompts } from '@/utils/labels';
|
import { prompts } from '@/utils/labels';
|
||||||
|
|
||||||
import { useInputUpdate } from '../../backend/useInputUpdate';
|
import { IOperationPosition } from '../../backend/api';
|
||||||
import { useOperationCreate } from '../../backend/useOperationCreate';
|
import { useOperationCreate } from '../../backend/useOperationCreate';
|
||||||
import { useOperationDelete } from '../../backend/useOperationDelete';
|
import { useOperationDelete } from '../../backend/useOperationDelete';
|
||||||
import { useOperationUpdate } from '../../backend/useOperationUpdate';
|
import { useOperationUpdate } from '../../backend/useOperationUpdate';
|
||||||
import { useOssSuspense } from '../../backend/useOSS';
|
import { useOssSuspense } from '../../backend/useOSS';
|
||||||
import { useRelocateConstituents } from '../../backend/useRelocateConstituents';
|
import { useRelocateConstituents } from '../../backend/useRelocateConstituents';
|
||||||
import { useUpdatePositions } from '../../backend/useUpdatePositions';
|
import { useUpdatePositions } from '../../backend/useUpdatePositions';
|
||||||
import { IOperationPosition, IOperationSchema, OperationID, OperationType } from '../../models/oss';
|
import { IOperationSchema, OperationID, OperationType } from '../../models/oss';
|
||||||
import { calculateInsertPosition } from '../../models/ossAPI';
|
import { calculateInsertPosition } from '../../models/ossAPI';
|
||||||
|
|
||||||
export enum OssTabID {
|
export enum OssTabID {
|
||||||
|
@ -106,7 +106,6 @@ export const OssEditState = ({ itemID, children }: React.PropsWithChildren<OssEd
|
||||||
const { operationDelete } = useOperationDelete();
|
const { operationDelete } = useOperationDelete();
|
||||||
const { operationUpdate } = useOperationUpdate();
|
const { operationUpdate } = useOperationUpdate();
|
||||||
const { relocateConstituents } = useRelocateConstituents();
|
const { relocateConstituents } = useRelocateConstituents();
|
||||||
const { inputUpdate } = useInputUpdate();
|
|
||||||
|
|
||||||
useEffect(
|
useEffect(
|
||||||
() =>
|
() =>
|
||||||
|
@ -223,16 +222,7 @@ export const OssEditState = ({ itemID, children }: React.PropsWithChildren<OssEd
|
||||||
showEditInput({
|
showEditInput({
|
||||||
oss: schema,
|
oss: schema,
|
||||||
target: operation,
|
target: operation,
|
||||||
onSubmit: (target, newInput) => {
|
positions: positions
|
||||||
inputUpdate({
|
|
||||||
itemID: schema.id,
|
|
||||||
data: {
|
|
||||||
target: target,
|
|
||||||
positions: positions,
|
|
||||||
input: newInput ?? null
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ import SelectLocation from '../../library/components/SelectLocation';
|
||||||
|
|
||||||
interface PickSchemaProps extends CProps.Styling {
|
interface PickSchemaProps extends CProps.Styling {
|
||||||
id?: string;
|
id?: string;
|
||||||
value?: LibraryItemID;
|
value: LibraryItemID | null;
|
||||||
onChange: (newValue: LibraryItemID) => void;
|
onChange: (newValue: LibraryItemID) => void;
|
||||||
|
|
||||||
initialFilter?: string;
|
initialFilter?: string;
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
import { Extension } from '@codemirror/state';
|
import { Extension } from '@codemirror/state';
|
||||||
import { hoverTooltip } from '@codemirror/view';
|
import { hoverTooltip, TooltipView } from '@codemirror/view';
|
||||||
|
import clsx from 'clsx';
|
||||||
|
|
||||||
import { findAliasAt } from '@/utils/codemirror';
|
import { findAliasAt } from '@/utils/codemirror';
|
||||||
import { domTooltipConstituenta } from '@/utils/codemirror';
|
import { labelCstTypification } from '@/utils/labels';
|
||||||
|
|
||||||
import { IRSForm } from '../../models/rsform';
|
import { IConstituenta, IRSForm } from '../../models/rsform';
|
||||||
|
import { isBasicConcept } from '../../models/rsformAPI';
|
||||||
|
|
||||||
const tooltipProducer = (schema: IRSForm, canClick?: boolean) => {
|
const tooltipProducer = (schema: IRSForm, canClick?: boolean) => {
|
||||||
return hoverTooltip((view, pos) => {
|
return hoverTooltip((view, pos) => {
|
||||||
|
@ -25,3 +27,78 @@ const tooltipProducer = (schema: IRSForm, canClick?: boolean) => {
|
||||||
export function rsHoverTooltip(schema: IRSForm, canClick?: boolean): Extension {
|
export function rsHoverTooltip(schema: IRSForm, canClick?: boolean): Extension {
|
||||||
return [tooltipProducer(schema, canClick)];
|
return [tooltipProducer(schema, canClick)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create DOM tooltip for {@link Constituenta}.
|
||||||
|
*/
|
||||||
|
function domTooltipConstituenta(cst?: IConstituenta, canClick?: boolean): TooltipView {
|
||||||
|
const dom = document.createElement('div');
|
||||||
|
dom.className = clsx(
|
||||||
|
'max-h-[25rem] max-w-[25rem] min-w-[10rem]',
|
||||||
|
'dense',
|
||||||
|
'p-2',
|
||||||
|
'border shadow-md',
|
||||||
|
'cc-scroll-y',
|
||||||
|
'text-sm font-main'
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!cst) {
|
||||||
|
const text = document.createElement('p');
|
||||||
|
text.innerText = 'Конституента не определена';
|
||||||
|
dom.appendChild(text);
|
||||||
|
} else {
|
||||||
|
const alias = document.createElement('p');
|
||||||
|
alias.className = 'font-math';
|
||||||
|
alias.style.overflowWrap = 'anywhere';
|
||||||
|
alias.innerHTML = `<b>${cst.alias}:</b> ${labelCstTypification(cst)}`;
|
||||||
|
dom.appendChild(alias);
|
||||||
|
|
||||||
|
if (cst.term_resolved) {
|
||||||
|
const term = document.createElement('p');
|
||||||
|
term.innerHTML = `<b>Термин:</b> ${cst.term_resolved}`;
|
||||||
|
dom.appendChild(term);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cst.definition_formal) {
|
||||||
|
const expression = document.createElement('p');
|
||||||
|
expression.innerHTML = `<b>Выражение:</b> ${cst.definition_formal}`;
|
||||||
|
dom.appendChild(expression);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cst.definition_resolved) {
|
||||||
|
const definition = document.createElement('p');
|
||||||
|
definition.innerHTML = `<b>Определение:</b> ${cst.definition_resolved}`;
|
||||||
|
dom.appendChild(definition);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cst.convention) {
|
||||||
|
const convention = document.createElement('p');
|
||||||
|
if (isBasicConcept(cst.cst_type)) {
|
||||||
|
convention.innerHTML = `<b>Конвенция:</b> ${cst.convention}`;
|
||||||
|
} else {
|
||||||
|
convention.innerHTML = `<b>Комментарий:</b> ${cst.convention}`;
|
||||||
|
}
|
||||||
|
dom.appendChild(convention);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cst.spawner_alias) {
|
||||||
|
const derived = document.createElement('p');
|
||||||
|
derived.innerHTML = `<b>Основание:</b> ${cst.spawner_alias}`;
|
||||||
|
dom.appendChild(derived);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cst.spawn_alias.length > 0) {
|
||||||
|
const children = document.createElement('p');
|
||||||
|
children.innerHTML = `<b>Порождает:</b> ${cst.spawn_alias.join(', ')}`;
|
||||||
|
dom.appendChild(children);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (canClick) {
|
||||||
|
const clickTip = document.createElement('p');
|
||||||
|
clickTip.className = 'text-center text-xs mt-1';
|
||||||
|
clickTip.innerText = 'Ctrl + клик для перехода';
|
||||||
|
dom.appendChild(clickTip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { dom: dom };
|
||||||
|
}
|
||||||
|
|
|
@ -1,16 +1,15 @@
|
||||||
import { syntaxTree } from '@codemirror/language';
|
import { syntaxTree } from '@codemirror/language';
|
||||||
import { Extension } from '@codemirror/state';
|
import { Extension } from '@codemirror/state';
|
||||||
import { hoverTooltip } from '@codemirror/view';
|
import { hoverTooltip, TooltipView } from '@codemirror/view';
|
||||||
|
import clsx from 'clsx';
|
||||||
|
|
||||||
import {
|
import { APP_COLORS, colorFgGrammeme } from '@/styling/color';
|
||||||
domTooltipEntityReference,
|
import { findContainedNodes, findReferenceAt } from '@/utils/codemirror';
|
||||||
domTooltipSyntacticReference,
|
import { describeConstituentaTerm, labelGrammeme } from '@/utils/labels';
|
||||||
findContainedNodes,
|
|
||||||
findReferenceAt
|
|
||||||
} from '@/utils/codemirror';
|
|
||||||
|
|
||||||
import { IEntityReference, ISyntacticReference } from '../../models/language';
|
import { IEntityReference, ISyntacticReference } from '../../models/language';
|
||||||
import { IRSForm } from '../../models/rsform';
|
import { parseGrammemes } from '../../models/languageAPI';
|
||||||
|
import { IConstituenta, IRSForm } from '../../models/rsform';
|
||||||
import { RefEntity } from './parse/parser.terms';
|
import { RefEntity } from './parse/parser.terms';
|
||||||
|
|
||||||
export const tooltipProducer = (schema: IRSForm, canClick?: boolean) => {
|
export const tooltipProducer = (schema: IRSForm, canClick?: boolean) => {
|
||||||
|
@ -56,3 +55,106 @@ export const tooltipProducer = (schema: IRSForm, canClick?: boolean) => {
|
||||||
export function refsHoverTooltip(schema: IRSForm, canClick?: boolean): Extension {
|
export function refsHoverTooltip(schema: IRSForm, canClick?: boolean): Extension {
|
||||||
return [tooltipProducer(schema, canClick)];
|
return [tooltipProducer(schema, canClick)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create DOM tooltip for {@link IEntityReference}.
|
||||||
|
*/
|
||||||
|
function domTooltipEntityReference(
|
||||||
|
ref: IEntityReference,
|
||||||
|
cst: IConstituenta | undefined,
|
||||||
|
canClick?: boolean
|
||||||
|
): TooltipView {
|
||||||
|
const dom = document.createElement('div');
|
||||||
|
dom.className = clsx(
|
||||||
|
'max-h-[25rem] max-w-[25rem] min-w-[10rem]',
|
||||||
|
'dense',
|
||||||
|
'p-2 flex flex-col',
|
||||||
|
'border shadow-md',
|
||||||
|
'cc-scroll-y',
|
||||||
|
'text-sm',
|
||||||
|
'select-none cursor-auto'
|
||||||
|
);
|
||||||
|
|
||||||
|
const header = document.createElement('p');
|
||||||
|
header.innerHTML = '<b>Ссылка на конституенту</b>';
|
||||||
|
dom.appendChild(header);
|
||||||
|
|
||||||
|
const term = document.createElement('p');
|
||||||
|
term.innerHTML = `<b>${ref.entity}:</b> ${describeConstituentaTerm(cst)}`;
|
||||||
|
dom.appendChild(term);
|
||||||
|
|
||||||
|
const grams = document.createElement('div');
|
||||||
|
grams.className = 'flex flex-wrap gap-1 mt-1';
|
||||||
|
parseGrammemes(ref.form).forEach(gramStr => {
|
||||||
|
const gram = document.createElement('div');
|
||||||
|
gram.id = `tooltip-${gramStr}`;
|
||||||
|
gram.className = clsx(
|
||||||
|
'min-w-[3rem]', //
|
||||||
|
'px-1',
|
||||||
|
'border rounded-md',
|
||||||
|
'text-sm text-center whitespace-nowrap'
|
||||||
|
);
|
||||||
|
gram.style.borderWidth = '1px';
|
||||||
|
gram.style.borderColor = colorFgGrammeme(gramStr);
|
||||||
|
gram.style.color = colorFgGrammeme(gramStr);
|
||||||
|
gram.style.fontWeight = '600';
|
||||||
|
gram.style.backgroundColor = APP_COLORS.bgInput;
|
||||||
|
gram.innerText = labelGrammeme(gramStr);
|
||||||
|
grams.appendChild(gram);
|
||||||
|
});
|
||||||
|
dom.appendChild(grams);
|
||||||
|
|
||||||
|
if (canClick) {
|
||||||
|
const clickTip = document.createElement('p');
|
||||||
|
clickTip.className = 'text-center text-xs mt-1';
|
||||||
|
clickTip.innerHTML = 'Ctrl + клик для перехода</br>Ctrl + пробел для редактирования';
|
||||||
|
dom.appendChild(clickTip);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { dom: dom };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create DOM tooltip for {@link ISyntacticReference}.
|
||||||
|
*/
|
||||||
|
function domTooltipSyntacticReference(
|
||||||
|
ref: ISyntacticReference,
|
||||||
|
masterRef: string | undefined,
|
||||||
|
canClick?: boolean
|
||||||
|
): TooltipView {
|
||||||
|
const dom = document.createElement('div');
|
||||||
|
dom.className = clsx(
|
||||||
|
'max-h-[25rem] max-w-[25rem] min-w-[10rem]',
|
||||||
|
'dense',
|
||||||
|
'p-2 flex flex-col',
|
||||||
|
'border shadow-md',
|
||||||
|
'cc-scroll-y',
|
||||||
|
'text-sm',
|
||||||
|
'select-none cursor-auto'
|
||||||
|
);
|
||||||
|
|
||||||
|
const header = document.createElement('p');
|
||||||
|
header.innerHTML = '<b>Связывание слов</b>';
|
||||||
|
dom.appendChild(header);
|
||||||
|
|
||||||
|
const offset = document.createElement('p');
|
||||||
|
offset.innerHTML = `<b>Смещение:</b> ${ref.offset}`;
|
||||||
|
dom.appendChild(offset);
|
||||||
|
|
||||||
|
const master = document.createElement('p');
|
||||||
|
master.innerHTML = `<b>Основная ссылка: </b> ${masterRef ?? 'не определена'}`;
|
||||||
|
dom.appendChild(master);
|
||||||
|
|
||||||
|
const nominal = document.createElement('p');
|
||||||
|
nominal.innerHTML = `<b>Начальная форма:</b> ${ref.nominal}`;
|
||||||
|
dom.appendChild(nominal);
|
||||||
|
|
||||||
|
if (canClick) {
|
||||||
|
const clickTip = document.createElement('p');
|
||||||
|
clickTip.className = 'text-center text-xs mt-1';
|
||||||
|
clickTip.innerHTML = 'Ctrl + пробел для редактирования';
|
||||||
|
dom.appendChild(clickTip);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { dom: dom };
|
||||||
|
}
|
||||||
|
|
|
@ -34,9 +34,7 @@ function DlgRenameCst() {
|
||||||
});
|
});
|
||||||
const alias = useWatch({ control, name: 'alias' });
|
const alias = useWatch({ control, name: 'alias' });
|
||||||
const cst_type = useWatch({ control, name: 'cst_type' });
|
const cst_type = useWatch({ control, name: 'cst_type' });
|
||||||
|
const isValid = alias !== target.alias && validateNewAlias(alias, cst_type, schema);
|
||||||
// TODO: validate in ZOD
|
|
||||||
const validated = alias !== target.alias && validateNewAlias(alias, cst_type, schema);
|
|
||||||
|
|
||||||
function onSubmit(data: ICstRenameDTO) {
|
function onSubmit(data: ICstRenameDTO) {
|
||||||
cstRename({ itemID: schema.id, data: data });
|
cstRename({ itemID: schema.id, data: data });
|
||||||
|
@ -52,7 +50,7 @@ function DlgRenameCst() {
|
||||||
header='Переименование конституенты'
|
header='Переименование конституенты'
|
||||||
submitText='Переименовать'
|
submitText='Переименовать'
|
||||||
submitInvalidTooltip='Введите незанятое имя, соответствующее типу'
|
submitInvalidTooltip='Введите незанятое имя, соответствующее типу'
|
||||||
canSubmit={validated}
|
canSubmit={isValid}
|
||||||
onSubmit={event => void handleSubmit(onSubmit)(event)}
|
onSubmit={event => void handleSubmit(onSubmit)(event)}
|
||||||
className={clsx('w-[30rem]', 'py-6 pr-3 pl-6 flex gap-3 justify-center items-center ')}
|
className={clsx('w-[30rem]', 'py-6 pr-3 pl-6 flex gap-3 justify-center items-center ')}
|
||||||
helpTopic={HelpTopic.CC_CONSTITUENTA}
|
helpTopic={HelpTopic.CC_CONSTITUENTA}
|
||||||
|
|
|
@ -3,26 +3,20 @@
|
||||||
*/
|
*/
|
||||||
import { syntaxTree } from '@codemirror/language';
|
import { syntaxTree } from '@codemirror/language';
|
||||||
import { NodeType, Tree, TreeCursor } from '@lezer/common';
|
import { NodeType, Tree, TreeCursor } from '@lezer/common';
|
||||||
import { EditorState, ReactCodeMirrorRef, SelectionRange, TooltipView } from '@uiw/react-codemirror';
|
import { EditorState, ReactCodeMirrorRef, SelectionRange } from '@uiw/react-codemirror';
|
||||||
import clsx from 'clsx';
|
|
||||||
|
|
||||||
import { ReferenceTokens } from '@/features/rsform/components/RefsInput/parse';
|
import { ReferenceTokens } from '@/features/rsform/components/RefsInput/parse';
|
||||||
import { RefEntity } from '@/features/rsform/components/RefsInput/parse/parser.terms';
|
import { RefEntity } from '@/features/rsform/components/RefsInput/parse/parser.terms';
|
||||||
import { GlobalTokens } from '@/features/rsform/components/RSInput/rslang';
|
import { GlobalTokens } from '@/features/rsform/components/RSInput/rslang';
|
||||||
import { IEntityReference, ISyntacticReference } from '@/features/rsform/models/language';
|
import { parseEntityReference, parseSyntacticReference } from '@/features/rsform/models/languageAPI';
|
||||||
import { parseEntityReference, parseGrammemes, parseSyntacticReference } from '@/features/rsform/models/languageAPI';
|
|
||||||
import { IConstituenta } from '@/features/rsform/models/rsform';
|
|
||||||
import { isBasicConcept } from '@/features/rsform/models/rsformAPI';
|
|
||||||
import { SyntaxTree } from '@/features/rsform/models/rslang';
|
import { SyntaxTree } from '@/features/rsform/models/rslang';
|
||||||
import { APP_COLORS, colorFgGrammeme } from '@/styling/color';
|
|
||||||
|
|
||||||
import { PARAMETER } from './constants';
|
import { PARAMETER } from './constants';
|
||||||
import { describeConstituentaTerm, labelCstTypification, labelGrammeme } from './labels';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents syntax tree node data.
|
* Represents syntax tree node data.
|
||||||
*/
|
*/
|
||||||
export interface SyntaxNode {
|
interface SyntaxNode {
|
||||||
type: NodeType;
|
type: NodeType;
|
||||||
from: number;
|
from: number;
|
||||||
to: number;
|
to: number;
|
||||||
|
@ -31,7 +25,7 @@ export interface SyntaxNode {
|
||||||
/**
|
/**
|
||||||
* Represents syntax tree cursor data.
|
* Represents syntax tree cursor data.
|
||||||
*/
|
*/
|
||||||
export interface CursorNode extends SyntaxNode {
|
interface CursorNode extends SyntaxNode {
|
||||||
isLeaf: boolean;
|
isLeaf: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,184 +208,6 @@ export function findReferenceAt(pos: number, state: EditorState) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Create DOM tooltip for {@link Constituenta}.
|
|
||||||
*/
|
|
||||||
export function domTooltipConstituenta(cst?: IConstituenta, canClick?: boolean): TooltipView {
|
|
||||||
const dom = document.createElement('div');
|
|
||||||
dom.className = clsx(
|
|
||||||
'max-h-[25rem] max-w-[25rem] min-w-[10rem]',
|
|
||||||
'dense',
|
|
||||||
'p-2',
|
|
||||||
'border shadow-md',
|
|
||||||
'cc-scroll-y',
|
|
||||||
'text-sm font-main'
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!cst) {
|
|
||||||
const text = document.createElement('p');
|
|
||||||
text.innerText = 'Конституента не определена';
|
|
||||||
dom.appendChild(text);
|
|
||||||
} else {
|
|
||||||
const alias = document.createElement('p');
|
|
||||||
alias.className = 'font-math';
|
|
||||||
alias.style.overflowWrap = 'anywhere';
|
|
||||||
alias.innerHTML = `<b>${cst.alias}:</b> ${labelCstTypification(cst)}`;
|
|
||||||
dom.appendChild(alias);
|
|
||||||
|
|
||||||
if (cst.term_resolved) {
|
|
||||||
const term = document.createElement('p');
|
|
||||||
term.innerHTML = `<b>Термин:</b> ${cst.term_resolved}`;
|
|
||||||
dom.appendChild(term);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cst.definition_formal) {
|
|
||||||
const expression = document.createElement('p');
|
|
||||||
expression.innerHTML = `<b>Выражение:</b> ${cst.definition_formal}`;
|
|
||||||
dom.appendChild(expression);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cst.definition_resolved) {
|
|
||||||
const definition = document.createElement('p');
|
|
||||||
definition.innerHTML = `<b>Определение:</b> ${cst.definition_resolved}`;
|
|
||||||
dom.appendChild(definition);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cst.convention) {
|
|
||||||
const convention = document.createElement('p');
|
|
||||||
if (isBasicConcept(cst.cst_type)) {
|
|
||||||
convention.innerHTML = `<b>Конвенция:</b> ${cst.convention}`;
|
|
||||||
} else {
|
|
||||||
convention.innerHTML = `<b>Комментарий:</b> ${cst.convention}`;
|
|
||||||
}
|
|
||||||
dom.appendChild(convention);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cst.spawner_alias) {
|
|
||||||
const derived = document.createElement('p');
|
|
||||||
derived.innerHTML = `<b>Основание:</b> ${cst.spawner_alias}`;
|
|
||||||
dom.appendChild(derived);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cst.spawn_alias.length > 0) {
|
|
||||||
const children = document.createElement('p');
|
|
||||||
children.innerHTML = `<b>Порождает:</b> ${cst.spawn_alias.join(', ')}`;
|
|
||||||
dom.appendChild(children);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (canClick) {
|
|
||||||
const clickTip = document.createElement('p');
|
|
||||||
clickTip.className = 'text-center text-xs mt-1';
|
|
||||||
clickTip.innerText = 'Ctrl + клик для перехода';
|
|
||||||
dom.appendChild(clickTip);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return { dom: dom };
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create DOM tooltip for {@link IEntityReference}.
|
|
||||||
*/
|
|
||||||
export function domTooltipEntityReference(
|
|
||||||
ref: IEntityReference,
|
|
||||||
cst: IConstituenta | undefined,
|
|
||||||
canClick?: boolean
|
|
||||||
): TooltipView {
|
|
||||||
const dom = document.createElement('div');
|
|
||||||
dom.className = clsx(
|
|
||||||
'max-h-[25rem] max-w-[25rem] min-w-[10rem]',
|
|
||||||
'dense',
|
|
||||||
'p-2 flex flex-col',
|
|
||||||
'border shadow-md',
|
|
||||||
'cc-scroll-y',
|
|
||||||
'text-sm',
|
|
||||||
'select-none cursor-auto'
|
|
||||||
);
|
|
||||||
|
|
||||||
const header = document.createElement('p');
|
|
||||||
header.innerHTML = '<b>Ссылка на конституенту</b>';
|
|
||||||
dom.appendChild(header);
|
|
||||||
|
|
||||||
const term = document.createElement('p');
|
|
||||||
term.innerHTML = `<b>${ref.entity}:</b> ${describeConstituentaTerm(cst)}`;
|
|
||||||
dom.appendChild(term);
|
|
||||||
|
|
||||||
const grams = document.createElement('div');
|
|
||||||
grams.className = 'flex flex-wrap gap-1 mt-1';
|
|
||||||
parseGrammemes(ref.form).forEach(gramStr => {
|
|
||||||
const gram = document.createElement('div');
|
|
||||||
gram.id = `tooltip-${gramStr}`;
|
|
||||||
gram.className = clsx(
|
|
||||||
'min-w-[3rem]', //
|
|
||||||
'px-1',
|
|
||||||
'border rounded-md',
|
|
||||||
'text-sm text-center whitespace-nowrap'
|
|
||||||
);
|
|
||||||
gram.style.borderWidth = '1px';
|
|
||||||
gram.style.borderColor = colorFgGrammeme(gramStr);
|
|
||||||
gram.style.color = colorFgGrammeme(gramStr);
|
|
||||||
gram.style.fontWeight = '600';
|
|
||||||
gram.style.backgroundColor = APP_COLORS.bgInput;
|
|
||||||
gram.innerText = labelGrammeme(gramStr);
|
|
||||||
grams.appendChild(gram);
|
|
||||||
});
|
|
||||||
dom.appendChild(grams);
|
|
||||||
|
|
||||||
if (canClick) {
|
|
||||||
const clickTip = document.createElement('p');
|
|
||||||
clickTip.className = 'text-center text-xs mt-1';
|
|
||||||
clickTip.innerHTML = 'Ctrl + клик для перехода</br>Ctrl + пробел для редактирования';
|
|
||||||
dom.appendChild(clickTip);
|
|
||||||
}
|
|
||||||
|
|
||||||
return { dom: dom };
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create DOM tooltip for {@link ISyntacticReference}.
|
|
||||||
*/
|
|
||||||
export function domTooltipSyntacticReference(
|
|
||||||
ref: ISyntacticReference,
|
|
||||||
masterRef: string | undefined,
|
|
||||||
canClick?: boolean
|
|
||||||
): TooltipView {
|
|
||||||
const dom = document.createElement('div');
|
|
||||||
dom.className = clsx(
|
|
||||||
'max-h-[25rem] max-w-[25rem] min-w-[10rem]',
|
|
||||||
'dense',
|
|
||||||
'p-2 flex flex-col',
|
|
||||||
'border shadow-md',
|
|
||||||
'cc-scroll-y',
|
|
||||||
'text-sm',
|
|
||||||
'select-none cursor-auto'
|
|
||||||
);
|
|
||||||
|
|
||||||
const header = document.createElement('p');
|
|
||||||
header.innerHTML = '<b>Связывание слов</b>';
|
|
||||||
dom.appendChild(header);
|
|
||||||
|
|
||||||
const offset = document.createElement('p');
|
|
||||||
offset.innerHTML = `<b>Смещение:</b> ${ref.offset}`;
|
|
||||||
dom.appendChild(offset);
|
|
||||||
|
|
||||||
const master = document.createElement('p');
|
|
||||||
master.innerHTML = `<b>Основная ссылка: </b> ${masterRef ?? 'не определена'}`;
|
|
||||||
dom.appendChild(master);
|
|
||||||
|
|
||||||
const nominal = document.createElement('p');
|
|
||||||
nominal.innerHTML = `<b>Начальная форма:</b> ${ref.nominal}`;
|
|
||||||
dom.appendChild(nominal);
|
|
||||||
|
|
||||||
if (canClick) {
|
|
||||||
const clickTip = document.createElement('p');
|
|
||||||
clickTip.className = 'text-center text-xs mt-1';
|
|
||||||
clickTip.innerHTML = 'Ctrl + пробел для редактирования';
|
|
||||||
dom.appendChild(clickTip);
|
|
||||||
}
|
|
||||||
|
|
||||||
return { dom: dom };
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapper class for CodeMirror editor.
|
* Wrapper class for CodeMirror editor.
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in New Issue
Block a user