2023-08-14 23:02:41 +03:00
|
|
|
|
2023-08-10 18:35:49 +03:00
|
|
|
import { Extension } from '@codemirror/state';
|
2023-08-13 00:57:31 +03:00
|
|
|
import { tags as t } from '@lezer/highlight';
|
2023-08-10 18:35:49 +03:00
|
|
|
import { createTheme } from '@uiw/codemirror-themes';
|
2023-08-12 01:03:06 +03:00
|
|
|
import CodeMirror, { BasicSetupOptions, ReactCodeMirrorProps, ReactCodeMirrorRef } from '@uiw/react-codemirror';
|
2023-08-10 18:35:49 +03:00
|
|
|
import { EditorView } from 'codemirror';
|
2023-08-16 18:32:37 +03:00
|
|
|
import { RefObject, useCallback, useMemo, useRef } from 'react';
|
2023-08-10 18:35:49 +03:00
|
|
|
|
2023-08-15 00:41:09 +03:00
|
|
|
import { useRSForm } from '../../context/RSFormContext';
|
2023-08-12 20:52:11 +03:00
|
|
|
import { useConceptTheme } from '../../context/ThemeContext';
|
2023-08-16 18:32:37 +03:00
|
|
|
import { TokenID } from '../../utils/enums';
|
|
|
|
import Label from '../Common/Label';
|
2023-08-14 23:02:41 +03:00
|
|
|
import { ccBracketMatching } from './bracketMatching';
|
2023-08-13 00:57:31 +03:00
|
|
|
import { RSLanguage } from './rslang';
|
2023-08-16 18:32:37 +03:00
|
|
|
import { getSymbolSubstitute,TextWrapper } from './textEditing';
|
2023-08-31 17:25:42 +03:00
|
|
|
import { rshoverTooltip as rsHoverTooltip } from './tooltip';
|
2023-08-10 18:35:49 +03:00
|
|
|
|
|
|
|
const editorSetup: BasicSetupOptions = {
|
2023-08-14 23:02:41 +03:00
|
|
|
highlightSpecialChars: false,
|
2023-08-10 18:35:49 +03:00
|
|
|
history: true,
|
2023-08-14 23:02:41 +03:00
|
|
|
drawSelection: false,
|
|
|
|
syntaxHighlighting: false,
|
2023-08-10 18:35:49 +03:00
|
|
|
defaultKeymap: true,
|
|
|
|
historyKeymap: true,
|
|
|
|
|
|
|
|
lineNumbers: false,
|
|
|
|
highlightActiveLineGutter: false,
|
|
|
|
foldGutter: false,
|
|
|
|
dropCursor: false,
|
|
|
|
allowMultipleSelections: false,
|
|
|
|
indentOnInput: false,
|
2023-08-12 01:03:06 +03:00
|
|
|
bracketMatching: false,
|
2023-08-10 18:35:49 +03:00
|
|
|
closeBrackets: false,
|
|
|
|
autocompletion: false,
|
|
|
|
rectangularSelection: false,
|
|
|
|
crosshairCursor: false,
|
|
|
|
highlightActiveLine: false,
|
|
|
|
highlightSelectionMatches: false,
|
|
|
|
closeBracketsKeymap: false,
|
|
|
|
searchKeymap: false,
|
|
|
|
foldKeymap: false,
|
|
|
|
completionKeymap: false,
|
|
|
|
lintKeymap: false
|
|
|
|
};
|
|
|
|
|
2023-08-12 01:03:06 +03:00
|
|
|
interface RSInputProps
|
2023-08-16 18:32:37 +03:00
|
|
|
extends Omit<ReactCodeMirrorProps, 'onChange'| 'onKeyDown'> {
|
|
|
|
label?: string
|
|
|
|
innerref?: RefObject<ReactCodeMirrorRef> | undefined
|
|
|
|
onChange?: (newValue: string) => void
|
2023-08-11 10:54:53 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
function RSInput({
|
2023-08-16 18:32:37 +03:00
|
|
|
id, label, innerref, onChange, editable,
|
2023-08-11 10:54:53 +03:00
|
|
|
...props
|
|
|
|
}: RSInputProps) {
|
2023-08-27 15:39:49 +03:00
|
|
|
const { darkMode, colors } = useConceptTheme();
|
2023-08-15 00:41:09 +03:00
|
|
|
const { schema } = useRSForm();
|
2023-08-10 18:35:49 +03:00
|
|
|
|
2023-08-16 18:32:37 +03:00
|
|
|
const internalRef = useRef<ReactCodeMirrorRef>(null);
|
|
|
|
const thisRef = useMemo(
|
|
|
|
() => {
|
|
|
|
return innerref ?? internalRef;
|
2023-08-23 22:57:25 +03:00
|
|
|
}, [internalRef, innerref]);
|
2023-08-16 18:32:37 +03:00
|
|
|
|
2023-08-12 01:03:06 +03:00
|
|
|
const cursor = useMemo(() => editable ? 'cursor-text': 'cursor-default', [editable]);
|
|
|
|
const lightTheme: Extension = useMemo(
|
|
|
|
() => createTheme({
|
|
|
|
theme: 'light',
|
|
|
|
settings: {
|
|
|
|
fontFamily: 'inherit',
|
2023-09-04 18:33:48 +03:00
|
|
|
background: editable ? colors.bgInput : colors.bgDefault,
|
2023-09-03 18:26:50 +03:00
|
|
|
foreground: colors.fgDefault,
|
|
|
|
selection: colors.bgHover
|
2023-08-12 01:03:06 +03:00
|
|
|
},
|
|
|
|
styles: [
|
2023-08-15 00:41:09 +03:00
|
|
|
{ tag: t.name, class: 'text-[#b266ff] cursor-default' }, // GlobalID
|
2023-08-13 00:57:31 +03:00
|
|
|
{ tag: t.variableName, class: 'text-[#24821a]' }, // LocalID
|
|
|
|
{ tag: t.propertyName, class: '' }, // Radical
|
|
|
|
{ tag: t.keyword, class: 'text-[#001aff]' }, // keywords
|
2023-08-14 18:40:19 +03:00
|
|
|
{ tag: t.literal, class: 'text-[#001aff]' }, // literals
|
2023-08-13 00:57:31 +03:00
|
|
|
{ tag: t.controlKeyword, class: 'font-semibold'}, // R | I | D
|
|
|
|
{ tag: t.unit, class: 'text-[0.75rem]' }, // indicies
|
2023-08-12 01:03:06 +03:00
|
|
|
]
|
2023-08-27 15:39:49 +03:00
|
|
|
}), [editable, colors]);
|
2023-08-12 01:03:06 +03:00
|
|
|
|
|
|
|
const darkTheme: Extension = useMemo(
|
|
|
|
() => createTheme({
|
|
|
|
theme: 'dark',
|
|
|
|
settings: {
|
|
|
|
fontFamily: 'inherit',
|
2023-09-04 18:33:48 +03:00
|
|
|
background: editable ? colors.bgInput : colors.bgDefault,
|
2023-09-03 18:26:50 +03:00
|
|
|
foreground: colors.fgDefault,
|
|
|
|
selection: colors.bgHover
|
2023-08-12 01:03:06 +03:00
|
|
|
},
|
|
|
|
styles: [
|
2023-08-15 00:41:09 +03:00
|
|
|
{ tag: t.name, class: 'text-[#dfbfff] cursor-default' }, // GlobalID
|
2023-08-13 00:57:31 +03:00
|
|
|
{ tag: t.variableName, class: 'text-[#69bf60]' }, // LocalID
|
|
|
|
{ tag: t.propertyName, class: '' }, // Radical
|
|
|
|
{ tag: t.keyword, class: 'text-[#808dff]' }, // keywords
|
2023-08-14 18:40:19 +03:00
|
|
|
{ tag: t.literal, class: 'text-[#808dff]' }, // literals
|
2023-08-13 00:57:31 +03:00
|
|
|
{ tag: t.controlKeyword, class: 'font-semibold'}, // R | I | D
|
|
|
|
{ tag: t.unit, class: 'text-[0.75rem]' }, // indicies
|
2023-08-12 01:03:06 +03:00
|
|
|
]
|
2023-08-27 15:39:49 +03:00
|
|
|
}), [editable, colors]);
|
2023-08-12 01:03:06 +03:00
|
|
|
|
2023-08-14 23:02:41 +03:00
|
|
|
const editorExtensions = useMemo(
|
|
|
|
() => [
|
|
|
|
EditorView.lineWrapping,
|
|
|
|
RSLanguage,
|
|
|
|
ccBracketMatching(darkMode),
|
2023-08-31 17:25:42 +03:00
|
|
|
rsHoverTooltip(schema?.items || []),
|
2023-08-15 00:41:09 +03:00
|
|
|
], [darkMode, schema?.items]);
|
2023-08-14 23:02:41 +03:00
|
|
|
|
2023-08-16 18:32:37 +03:00
|
|
|
const handleInput = useCallback(
|
|
|
|
(event: React.KeyboardEvent<HTMLDivElement>) => {
|
|
|
|
if (!thisRef.current) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const text = new TextWrapper(thisRef.current as Required<ReactCodeMirrorRef>);
|
|
|
|
if (event.shiftKey && event.key === '*' && !event.altKey) {
|
|
|
|
text.insertToken(TokenID.DECART);
|
|
|
|
} else if (event.altKey) {
|
2023-09-06 13:10:14 +03:00
|
|
|
if (!text.processAltKey(event.code, event.shiftKey)) {
|
2023-08-16 18:32:37 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else if (!event.ctrlKey) {
|
2023-09-06 13:10:14 +03:00
|
|
|
const newSymbol = getSymbolSubstitute(event.code, event.shiftKey);
|
2023-08-16 18:32:37 +03:00
|
|
|
if (!newSymbol) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
text.replaceWith(newSymbol);
|
|
|
|
} else {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
event.preventDefault();
|
|
|
|
}, [thisRef]);
|
|
|
|
|
2023-08-10 18:35:49 +03:00
|
|
|
return (
|
2023-09-03 18:26:50 +03:00
|
|
|
<div className={`flex flex-col w-full ${cursor}`}>
|
2023-08-16 18:32:37 +03:00
|
|
|
{label &&
|
|
|
|
<Label
|
|
|
|
text={label}
|
|
|
|
required={false}
|
|
|
|
htmlFor={id}
|
2023-09-03 18:26:50 +03:00
|
|
|
className='mb-2'
|
2023-08-16 18:32:37 +03:00
|
|
|
/>}
|
|
|
|
<CodeMirror id={id}
|
|
|
|
ref={thisRef}
|
2023-08-10 18:35:49 +03:00
|
|
|
basicSetup={editorSetup}
|
2023-08-14 23:02:41 +03:00
|
|
|
theme={darkMode ? darkTheme : lightTheme}
|
2023-08-11 10:54:53 +03:00
|
|
|
extensions={editorExtensions}
|
2023-08-10 18:35:49 +03:00
|
|
|
indentWithTab={false}
|
2023-08-16 18:32:37 +03:00
|
|
|
onChange={onChange}
|
2023-08-12 01:03:06 +03:00
|
|
|
editable={editable}
|
2023-08-16 18:32:37 +03:00
|
|
|
onKeyDown={handleInput}
|
2023-08-11 10:54:53 +03:00
|
|
|
{...props}
|
2023-08-10 18:35:49 +03:00
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
export default RSInput;
|