UNPKG

@vectara/vectara-ui

Version:

Vectara's design system, codified as a React and Sass component library

118 lines (117 loc) 6.92 kB
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."] })) }) }))] }))] })); };