Files
HiveGrid-Wireframes/hivegrid-cloud/apps/web/app/components/editors/MonacoEditor.client.tsx
T

149 lines
4.2 KiB
TypeScript

import { Editor, useMonaco } from "@monaco-editor/react";
import clsx from "clsx";
import { Fragment, useEffect } from "react";
import { useTranslation } from "react-i18next";
export type MonacoAutoCompletion = {
label: string;
kind: any;
documentation: string;
insertText: string;
insertTextRules?: any;
};
export interface MonacoEditorClientProps {
name?: string;
value: string;
onChange?: (value: string) => void;
theme?: "vs-dark" | "light";
hideLineNumbers?: boolean;
language?: "javascript" | "typescript" | "html" | "css" | "json" | "markdown" | "yaml" | "sql";
fontSize?: number;
className?: string;
autocompletions?: MonacoAutoCompletion[];
tabSize?: number;
readOnly?: boolean;
options?: Record<string, unknown>;
defaultValue?: string;
defaultLanguage?: string;
onMount?: (editor: any, monaco: any) => void;
onControlEnter?: () => void;
}
let registerCompletion: any;
export default function MonacoEditorClient({
name,
value,
onChange,
theme,
hideLineNumbers,
language,
fontSize,
className,
autocompletions,
tabSize = 4,
readOnly,
options,
defaultValue,
defaultLanguage,
onMount,
onControlEnter,
}: MonacoEditorClientProps) {
const monaco = useMonaco();
const { t } = useTranslation();
useEffect(() => {
function createDependencyProposals(autocompletions: MonacoAutoCompletion[], range: any) {
return autocompletions.map((f) => {
return {
...f,
range: range,
};
});
}
if (monaco) {
if (onControlEnter) {
const executeAction: any = {
id: "onControlEnter",
label: t("components.editor.actions.execute"),
contextMenuOrder: 2,
contextMenuGroupId: "1_modification",
keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter],
run: onControlEnter,
};
monaco.editor.addEditorAction(executeAction);
}
if (registerCompletion) {
registerCompletion.dispose();
}
registerCompletion = monaco.languages.registerCompletionItemProvider("markdown", {
provideCompletionItems: function (model: any, position: any) {
// find out if we are completing a property in the 'dependencies' object.
// var textUntilPosition = model.getValueInRange({
// startLineNumber: 1,
// startColumn: 1,
// endLineNumber: position.lineNumber,
// endColumn: position.column,
// });
// var match = textUntilPosition.match(/"dependencies"\s*:\s*\{\s*("[^"]*"\s*:\s*"[^"]*"\s*,\s*)*([^"]*)?$/);
// if (!match) {
// return { suggestions: [] };
// }
var word = model.getWordUntilPosition(position);
var range = {
startLineNumber: position.lineNumber,
endLineNumber: position.lineNumber,
startColumn: word.startColumn,
endColumn: word.endColumn,
};
return {
suggestions: createDependencyProposals(autocompletions ?? [], range),
};
},
});
}
return () => {
if (registerCompletion) {
registerCompletion.dispose();
}
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [autocompletions, monaco]);
return (
<Fragment>
{name && <textarea name={name} value={value} hidden readOnly />}
{typeof window !== "undefined" && (
<Editor
loading={<div className={clsx(className)}></div>}
theme={theme}
className={clsx(
className,
"focus:border-border focus:ring-ring border-border block w-full min-w-0 flex-1 rounded-md sm:text-sm",
hideLineNumbers && "-ml-10"
)}
defaultLanguage={defaultLanguage}
language={language}
options={{
fontSize,
renderValidationDecorations: "off",
wordWrap: "on",
unusualLineTerminators: "off",
tabSize,
readOnly,
...options,
}}
value={value}
defaultValue={defaultValue}
onMount={onMount}
onChange={(e) => (onChange ? onChange(e ?? "") : null)}
/>
)}
</Fragment>
);
}