Add tooltips

This commit is contained in:
IRBorisov 2023-08-15 00:41:09 +03:00
parent 54b10e4bf6
commit cc882ad53a
3 changed files with 103 additions and 75 deletions

View File

@ -1,19 +1,20 @@
import { bracketMatching, MatchResult } from '@codemirror/language'; import { bracketMatching, MatchResult } from '@codemirror/language';
import { Decoration, EditorView } from '@codemirror/view'; import { Decoration, EditorView } from '@codemirror/view';
const matchingMark = Decoration.mark({class: "cc-matchingBracket"}), const matchingMark = Decoration.mark({class: "cc-matchingBracket"});
nonmatchingMark = Decoration.mark({class: "cc-nonmatchingBracket"}) const nonmatchingMark = Decoration.mark({class: "cc-nonmatchingBracket"});
function bracketRender(match: MatchResult) { function bracketRender(match: MatchResult) {
const decorations = [] const decorations = [];
const mark = match.matched ? matchingMark : nonmatchingMark const mark = match.matched ? matchingMark : nonmatchingMark;
decorations.push(mark.range(match.start.from, match.start.to)) decorations.push(mark.range(match.start.from, match.start.to));
if (match.end) decorations.push(mark.range(match.end.from, match.end.to)) if (match.end) {
return decorations decorations.push(mark.range(match.end.from, match.end.to));
}
return decorations;
} }
export function ccBracketMatching(darkMode: boolean) { const darkTheme = EditorView.baseTheme({
const bracketTheme = EditorView.baseTheme({
'.cc-matchingBracket': { '.cc-matchingBracket': {
fontWeight: 600, fontWeight: 600,
}, },
@ -22,9 +23,23 @@ export function ccBracketMatching(darkMode: boolean) {
fontWeight: 700, fontWeight: 700,
}, },
'&.cm-focused .cc-matchingBracket': { '&.cm-focused .cc-matchingBracket': {
backgroundColor: darkMode ? '#734f00' : '#dae6f2', backgroundColor: '#734f00',
}, },
}); });
return [bracketMatching({ renderMatch: bracketRender }), bracketTheme]; const lightTheme = EditorView.baseTheme({
'.cc-matchingBracket': {
fontWeight: 600,
},
'.cc-nonmatchingBracket': {
color: '#ef4444',
fontWeight: 700,
},
'&.cm-focused .cc-matchingBracket': {
backgroundColor: '#dae6f2',
},
});
export function ccBracketMatching(darkMode: boolean) {
return [bracketMatching({ renderMatch: bracketRender }), darkMode ? darkTheme : lightTheme];
} }

View File

@ -6,10 +6,11 @@ import CodeMirror, { BasicSetupOptions, ReactCodeMirrorProps, ReactCodeMirrorRef
import { EditorView } from 'codemirror'; import { EditorView } from 'codemirror';
import { Ref, useMemo } from 'react'; import { Ref, useMemo } from 'react';
import { useRSForm } from '../../context/RSFormContext';
import { useConceptTheme } from '../../context/ThemeContext'; import { useConceptTheme } from '../../context/ThemeContext';
import { ccBracketMatching } from './bracketMatching'; import { ccBracketMatching } from './bracketMatching';
import { RSLanguage } from './rslang'; import { RSLanguage } from './rslang';
//import { cursorTooltip } from './tooltip'; import { rshoverTooltip } from './tooltip';
const editorSetup: BasicSetupOptions = { const editorSetup: BasicSetupOptions = {
highlightSpecialChars: false, highlightSpecialChars: false,
@ -51,6 +52,7 @@ function RSInput({
...props ...props
}: RSInputProps) { }: RSInputProps) {
const { darkMode } = useConceptTheme(); const { darkMode } = useConceptTheme();
const { schema } = useRSForm();
const cursor = useMemo(() => editable ? 'cursor-text': 'cursor-default', [editable]); const cursor = useMemo(() => editable ? 'cursor-text': 'cursor-default', [editable]);
const lightTheme: Extension = useMemo( const lightTheme: Extension = useMemo(
@ -64,7 +66,7 @@ function RSInput({
caret: '#5d00ff', caret: '#5d00ff',
}, },
styles: [ styles: [
{ tag: t.name, class: 'text-[#b266ff]' }, // GlobalID { tag: t.name, class: 'text-[#b266ff] cursor-default' }, // GlobalID
{ tag: t.variableName, class: 'text-[#24821a]' }, // LocalID { tag: t.variableName, class: 'text-[#24821a]' }, // LocalID
{ tag: t.propertyName, class: '' }, // Radical { tag: t.propertyName, class: '' }, // Radical
{ tag: t.keyword, class: 'text-[#001aff]' }, // keywords { tag: t.keyword, class: 'text-[#001aff]' }, // keywords
@ -85,7 +87,7 @@ function RSInput({
caret: '#ffaa00' caret: '#ffaa00'
}, },
styles: [ styles: [
{ tag: t.name, class: 'text-[#dfbfff]' }, // GlobalID { tag: t.name, class: 'text-[#dfbfff] cursor-default' }, // GlobalID
{ tag: t.variableName, class: 'text-[#69bf60]' }, // LocalID { tag: t.variableName, class: 'text-[#69bf60]' }, // LocalID
{ tag: t.propertyName, class: '' }, // Radical { tag: t.propertyName, class: '' }, // Radical
{ tag: t.keyword, class: 'text-[#808dff]' }, // keywords { tag: t.keyword, class: 'text-[#808dff]' }, // keywords
@ -100,8 +102,8 @@ function RSInput({
EditorView.lineWrapping, EditorView.lineWrapping,
RSLanguage, RSLanguage,
ccBracketMatching(darkMode), ccBracketMatching(darkMode),
//cursorTooltip(), rshoverTooltip(schema?.items || []),
], [darkMode]); ], [darkMode, schema?.items]);
return ( return (
<div className={`w-full ${cursor} text-lg`}> <div className={`w-full ${cursor} text-lg`}>

View File

@ -1,53 +1,64 @@
import { EditorState, Extension, StateField } from "@codemirror/state"; import { Extension } from "@codemirror/state";
import { EditorView, showTooltip } from "@codemirror/view"; import { hoverTooltip } from "@codemirror/view";
function getCursorTooltips(state: EditorState) { import { IConstituenta } from '../../utils/models';
return state.selection.ranges import { getCstTypificationLabel } from '../../utils/staticUI';
.filter(range => !range.empty)
.map(range => { function createTooltipFor(cst: IConstituenta) {
const line = state.doc.lineAt(range.head); const dom = document.createElement('div');
const text = `${line.number}:${range.head - line.from}`; dom.className = 'overflow-y-auto border shadow-md max-h-[25rem] max-w-[25rem] min-w-[10rem] w-fit z-20 text-sm';
const alias = document.createElement('h1');
alias.className = 'text-sm text-left';
alias.textContent = `${cst.alias}: ${getCstTypificationLabel(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.text.resolved) {
const definition = document.createElement('p');
definition.innerHTML = `<b>Определение:</b> ${cst.definition.text.resolved}`;
dom.appendChild(definition);
}
if (cst.convention) {
const convention = document.createElement('p');
convention.innerHTML = `<b>Конвенция:</b> ${cst.convention}`;
dom.appendChild(convention);
}
return { dom: dom }
}
export const getHoverTooltip = (items: IConstituenta[]) => {
return hoverTooltip((view, pos, side) => {
const {from, to, text} = view.state.doc.lineAt(pos);
let start = pos, end = pos;
while (start > from && /\w/.test(text[start - from - 1]))
start--;
while (end < to && /\w/.test(text[end - from]))
end++;
if (start == pos && side < 0 || end == pos && side > 0) {
return null;
}
const alias = text.slice(start - from, end - from);
const cst = items.find(cst => cst.alias === alias);
if (!cst) {
return null;
}
return { return {
pos: (range.to + range.from)/2, pos: start,
end: end,
above: false, above: false,
strictSide: true, create: () => createTooltipFor(cst)
create: () => {
const dom = document.createElement("div");
dom.className = "cm-tooltip-cursor";
dom.textContent = text;
return { dom };
} }
};
}); });
} }
const cursorTooltipField = StateField.define({ export function rshoverTooltip(items: IConstituenta[]): Extension {
create: getCursorTooltips, return [getHoverTooltip(items)];
update(tooltips, transaction) {
if (!transaction.docChanged && !transaction.selection) {
return tooltips;
}
return getCursorTooltips(transaction.state);
},
provide: field => showTooltip.computeN([field], state => state.field(field))
});
const cursorTooltipBaseTheme = EditorView.baseTheme({
".cm-tooltip.cm-tooltip-cursor": {
backgroundColor: "#66b",
color: "white",
border: "none",
padding: "2px 7px",
borderRadius: "4px",
"&.cm-tooltip-arrow:before": {
borderTopColor: "#66b"
},
"&.cm-tooltip-arrow:after": {
borderTopColor: "transparent"
}
}
});
export function cursorTooltip(): Extension {
return [cursorTooltipField, cursorTooltipBaseTheme];
} }