@vectara/vectara-ui
Version:
Vectara's design system, codified as a React and Sass component library
118 lines (117 loc) • 6.92 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
import Editor from "@monaco-editor/react";
import * as monacoTypes from "monaco-editor/esm/vs/editor/editor.api";
import { useEffect, useId, useRef } from "react";
import { VuiText } from "../../typography/Text";
import { VuiTextColor } from "../../typography/TextColor";
import { VuiSpacer } from "../../spacer/Spacer";
const isMac = typeof navigator !== "undefined" && /Mac|iPhone|iPad|iPod/i.test(navigator.userAgent);
// Mac vs Linux/Windows
const SUGGEST_SHORTCUT_LABEL = isMac ? "⌃ Space" : "Ctrl + Space";
/**
* Generic (to console) code editor that wraps React Monaco Editor.
* As the need arises, this component can accept props to customize the internal Monaco editor options.
*/
export const VuiCodeEditor = ({ language: languageProp, TokensProvider, onChange, placeholder, onError = (errors) => {
/*noop*/
}, colorConfig, value, defaultValue, error, isReadOnly, height = "300px", resizable = false, completionItemProvider, quickSuggestions = false, "data-testid": testId }) => {
const instanceId = useId().replace(/:/g, "");
const language = `${languageProp}-${instanceId}`;
const tokensProvider = TokensProvider ? new TokensProvider() : null;
const monacoRef = useRef(null);
const modelRef = useRef(null);
const completionProviderRef = useRef(completionItemProvider);
useEffect(() => {
completionProviderRef.current = completionItemProvider;
}, [completionItemProvider]);
if (modelRef.current && monacoRef.current) {
if (error) {
const markers = [
{
severity: monacoRef.current.MarkerSeverity.Error,
startLineNumber: error.startLine,
startColumn: error.startColumn,
endLineNumber: error.endLine,
endColumn: error.endColumn,
message: error.message
}
];
monacoRef.current.editor.setModelMarkers(modelRef.current, language, markers);
}
else {
monacoRef.current.editor.setModelMarkers(modelRef.current, language, []);
}
}
const init = (monaco) => {
monacoRef.current = monaco;
monaco.languages.register({ id: language });
if (tokensProvider) {
monaco.languages.setTokensProvider(language, tokensProvider);
}
if (completionProviderRef.current) {
monaco.languages.registerCompletionItemProvider(language, {
triggerCharacters: completionProviderRef.current.triggerCharacters,
provideCompletionItems: (model, position, context, token) => { var _a, _b; return (_b = (_a = completionProviderRef.current) === null || _a === void 0 ? void 0 : _a.provideCompletionItems(model, position, context, token)) !== null && _b !== void 0 ? _b : { suggestions: [] }; },
resolveCompletionItem: (item, token) => { var _a, _b, _c; return (_c = (_b = (_a = completionProviderRef.current) === null || _a === void 0 ? void 0 : _a.resolveCompletionItem) === null || _b === void 0 ? void 0 : _b.call(_a, item, token)) !== null && _c !== void 0 ? _c : item; }
});
}
monaco.editor.defineTheme("vectaraEditor", {
base: "vs",
inherit: true,
rules: colorConfig !== null && colorConfig !== void 0 ? colorConfig : [],
colors: {}
});
monaco.languages.setLanguageConfiguration(language, {
autoClosingPairs: [
{ open: "(", close: ")" },
{ open: "[", close: "]" },
{ open: "{", close: "}" },
{ open: "'", close: "'", notIn: ["string"] }
],
colorizedBracketPairs: [
["(", ")"],
["[", "]"],
["(", ")"]
]
});
monaco.editor.onDidCreateModel((model) => (modelRef.current = model));
};
const shouldShowPlaceholder = placeholder && (!value || value.trim() === "") && (!defaultValue || defaultValue.trim() === "");
return (_jsxs(_Fragment, { children: [_jsxs("div", Object.assign({ className: "vuiCodeEditor", "data-testid": testId, style: resizable ? { minHeight: height, height, resize: "vertical", overflow: "auto" } : undefined }, { children: [shouldShowPlaceholder && (_jsx("div", Object.assign({ className: "vuiCodeEditor-placeholder", "aria-hidden": "true" }, { children: placeholder }))), _jsx(Editor, { beforeMount: init, height: resizable ? "100%" : height, width: "100%", defaultLanguage: language, defaultValue: defaultValue, value: value, onChange: onChange, onValidate: (markers) => {
const errors = markers.reduce((acc, marker) => {
if (marker.severity === monacoTypes.MarkerSeverity.Error) {
acc.push({
startLine: marker.startLineNumber,
endLine: marker.endLineNumber,
startColumn: marker.startColumn,
endColumn: marker.endColumn,
message: marker.message
});
}
return acc;
}, []);
if (errors.length) {
onError(errors);
}
}, theme: "vectaraEditor", options: {
readOnly: isReadOnly,
fontSize: 12,
automaticLayout: true,
renderLineHighlight: "none",
lineNumbers: "off",
glyphMargin: false,
folding: false,
lineDecorationsWidth: 2,
fixedOverflowWidgets: true,
quickSuggestions: {
comments: false,
strings: false,
other: quickSuggestions
},
suggestOnTriggerCharacters: true,
minimap: {
enabled: false
},
wordWrap: "on"
} })] })), completionItemProvider && (_jsxs(_Fragment, { children: [_jsx(VuiSpacer, { size: "xs" }), _jsx(VuiText, Object.assign({ size: "xs" }, { children: _jsx("p", { children: _jsxs(VuiTextColor, Object.assign({ color: "subdued" }, { children: [_jsx("kbd", { children: SUGGEST_SHORTCUT_LABEL }), " shows suggestions."] })) }) }))] }))] }));
};