diff --git a/rsconcept/frontend/src/features/rsform/components/RSInput/tooltip.ts b/rsconcept/frontend/src/features/rsform/components/RSInput/tooltip.ts index ea38c29f..07e5e76d 100644 --- a/rsconcept/frontend/src/features/rsform/components/RSInput/tooltip.ts +++ b/rsconcept/frontend/src/features/rsform/components/RSInput/tooltip.ts @@ -1,10 +1,12 @@ 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 { 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) => { return hoverTooltip((view, pos) => { @@ -25,3 +27,78 @@ const tooltipProducer = (schema: IRSForm, canClick?: boolean) => { export function rsHoverTooltip(schema: IRSForm, canClick?: boolean): Extension { 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 = `${cst.alias}: ${labelCstTypification(cst)}`; + dom.appendChild(alias); + + if (cst.term_resolved) { + const term = document.createElement('p'); + term.innerHTML = `Термин: ${cst.term_resolved}`; + dom.appendChild(term); + } + + if (cst.definition_formal) { + const expression = document.createElement('p'); + expression.innerHTML = `Выражение: ${cst.definition_formal}`; + dom.appendChild(expression); + } + + if (cst.definition_resolved) { + const definition = document.createElement('p'); + definition.innerHTML = `Определение: ${cst.definition_resolved}`; + dom.appendChild(definition); + } + + if (cst.convention) { + const convention = document.createElement('p'); + if (isBasicConcept(cst.cst_type)) { + convention.innerHTML = `Конвенция: ${cst.convention}`; + } else { + convention.innerHTML = `Комментарий: ${cst.convention}`; + } + dom.appendChild(convention); + } + + if (cst.spawner_alias) { + const derived = document.createElement('p'); + derived.innerHTML = `Основание: ${cst.spawner_alias}`; + dom.appendChild(derived); + } + + if (cst.spawn_alias.length > 0) { + const children = document.createElement('p'); + children.innerHTML = `Порождает: ${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 }; +} diff --git a/rsconcept/frontend/src/features/rsform/components/RefsInput/tooltip.ts b/rsconcept/frontend/src/features/rsform/components/RefsInput/tooltip.ts index 4a91032f..845fae9e 100644 --- a/rsconcept/frontend/src/features/rsform/components/RefsInput/tooltip.ts +++ b/rsconcept/frontend/src/features/rsform/components/RefsInput/tooltip.ts @@ -1,16 +1,15 @@ import { syntaxTree } from '@codemirror/language'; import { Extension } from '@codemirror/state'; -import { hoverTooltip } from '@codemirror/view'; +import { hoverTooltip, TooltipView } from '@codemirror/view'; +import clsx from 'clsx'; -import { - domTooltipEntityReference, - domTooltipSyntacticReference, - findContainedNodes, - findReferenceAt -} from '@/utils/codemirror'; +import { APP_COLORS, colorFgGrammeme } from '@/styling/color'; +import { findContainedNodes, findReferenceAt } from '@/utils/codemirror'; +import { describeConstituentaTerm, labelGrammeme } from '@/utils/labels'; 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'; 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 { 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 = 'Ссылка на конституенту'; + dom.appendChild(header); + + const term = document.createElement('p'); + term.innerHTML = `${ref.entity}: ${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 + клик для перехода
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 = 'Связывание слов'; + dom.appendChild(header); + + const offset = document.createElement('p'); + offset.innerHTML = `Смещение: ${ref.offset}`; + dom.appendChild(offset); + + const master = document.createElement('p'); + master.innerHTML = `Основная ссылка: ${masterRef ?? 'не определена'}`; + dom.appendChild(master); + + const nominal = document.createElement('p'); + nominal.innerHTML = `Начальная форма: ${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 }; +} diff --git a/rsconcept/frontend/src/utils/codemirror.ts b/rsconcept/frontend/src/utils/codemirror.ts index adf778f2..5d6e6521 100644 --- a/rsconcept/frontend/src/utils/codemirror.ts +++ b/rsconcept/frontend/src/utils/codemirror.ts @@ -3,26 +3,20 @@ */ import { syntaxTree } from '@codemirror/language'; import { NodeType, Tree, TreeCursor } from '@lezer/common'; -import { EditorState, ReactCodeMirrorRef, SelectionRange, TooltipView } from '@uiw/react-codemirror'; -import clsx from 'clsx'; +import { EditorState, ReactCodeMirrorRef, SelectionRange } from '@uiw/react-codemirror'; import { ReferenceTokens } from '@/features/rsform/components/RefsInput/parse'; import { RefEntity } from '@/features/rsform/components/RefsInput/parse/parser.terms'; import { GlobalTokens } from '@/features/rsform/components/RSInput/rslang'; -import { IEntityReference, ISyntacticReference } from '@/features/rsform/models/language'; -import { parseEntityReference, parseGrammemes, parseSyntacticReference } from '@/features/rsform/models/languageAPI'; -import { IConstituenta } from '@/features/rsform/models/rsform'; -import { isBasicConcept } from '@/features/rsform/models/rsformAPI'; +import { parseEntityReference, parseSyntacticReference } from '@/features/rsform/models/languageAPI'; import { SyntaxTree } from '@/features/rsform/models/rslang'; -import { APP_COLORS, colorFgGrammeme } from '@/styling/color'; import { PARAMETER } from './constants'; -import { describeConstituentaTerm, labelCstTypification, labelGrammeme } from './labels'; /** * Represents syntax tree node data. */ -export interface SyntaxNode { +interface SyntaxNode { type: NodeType; from: number; to: number; @@ -31,7 +25,7 @@ export interface SyntaxNode { /** * Represents syntax tree cursor data. */ -export interface CursorNode extends SyntaxNode { +interface CursorNode extends SyntaxNode { 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 = `${cst.alias}: ${labelCstTypification(cst)}`; - dom.appendChild(alias); - - if (cst.term_resolved) { - const term = document.createElement('p'); - term.innerHTML = `Термин: ${cst.term_resolved}`; - dom.appendChild(term); - } - - if (cst.definition_formal) { - const expression = document.createElement('p'); - expression.innerHTML = `Выражение: ${cst.definition_formal}`; - dom.appendChild(expression); - } - - if (cst.definition_resolved) { - const definition = document.createElement('p'); - definition.innerHTML = `Определение: ${cst.definition_resolved}`; - dom.appendChild(definition); - } - - if (cst.convention) { - const convention = document.createElement('p'); - if (isBasicConcept(cst.cst_type)) { - convention.innerHTML = `Конвенция: ${cst.convention}`; - } else { - convention.innerHTML = `Комментарий: ${cst.convention}`; - } - dom.appendChild(convention); - } - - if (cst.spawner_alias) { - const derived = document.createElement('p'); - derived.innerHTML = `Основание: ${cst.spawner_alias}`; - dom.appendChild(derived); - } - - if (cst.spawn_alias.length > 0) { - const children = document.createElement('p'); - children.innerHTML = `Порождает: ${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 = 'Ссылка на конституенту'; - dom.appendChild(header); - - const term = document.createElement('p'); - term.innerHTML = `${ref.entity}: ${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 + клик для перехода
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 = 'Связывание слов'; - dom.appendChild(header); - - const offset = document.createElement('p'); - offset.innerHTML = `Смещение: ${ref.offset}`; - dom.appendChild(offset); - - const master = document.createElement('p'); - master.innerHTML = `Основная ссылка: ${masterRef ?? 'не определена'}`; - dom.appendChild(master); - - const nominal = document.createElement('p'); - nominal.innerHTML = `Начальная форма: ${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. *