GithubHelp home page GithubHelp logo

suren-atoyan / monaco-react Goto Github PK

View Code? Open in Web Editor NEW
3.4K 17.0 250.0 1.52 MB

Monaco Editor for React - use the monaco-editor in any React application without needing to use webpack (or rollup/parcel/etc) configuration files / plugins

Home Page: https://monaco-react.surenatoyan.com/

License: MIT License

HTML 1.02% CSS 1.25% TypeScript 97.73%
monaco editor react vscode

monaco-react's Introduction

@monaco-editor/react · monthly downloads gitHub license npm version PRs welcome

Monaco Editor for React · use the monaco-editor in any React application without needing to use webpack (or rollup/parcel/etc) configuration files / plugins


  • ⌨️ rewritten with TypeScript 🔥
  • multi-model editor is already supported; enjoy it 🎉
  • 🎉 version v4 is here - to see what's new in the new version and how to migrate from v3, please read this doc (also, if you need the old version README, it's here)
  • 🎮 the new section Development / Playground has been created - now you can run the playground and play with the internals of the library
  • 💫 it's already integrated with @monaco-editor/loader

Synopsis

Monaco editor wrapper for easy/one-line integration with any React application without needing to use webpack (or any other module bundler) configuration files / plugins. It can be used with apps generated by create-react-app, create-snowpack-app, vite, Next.js or any other app generators - you don't need to eject or rewire them.

Motivation

The monaco-editor is a well-known web technology based code editor that powers VS Code. This library handles the setup process of the monaco-editor and provides a clean API to interact with monaco from any React environment

Demo

Check it out!

Documentation

Installation

npm install @monaco-editor/react

or

yarn add @monaco-editor/react

or you can use CDN. Here is an example

NOTE: For TypeScript type definitions, this package uses the monaco-editor package as a peer dependency. So, if you need types and don't already have the monaco-editor package installed, you will need to do so

Introduction

Besides types, the library exports Editorand DiffEditor components, as well as the loader utility and the useMonaco hook:

import Editor, { DiffEditor, useMonaco, loader } from '@monaco-editor/react';

Usage

Simple usage

Here is an example of a simple integration of monaco editor with a React project.
You just need to import and render the Editor component:

import React from 'react';
import ReactDOM from 'react-dom';

import Editor from '@monaco-editor/react';

function App() {
  return <Editor height="90vh" defaultLanguage="javascript" defaultValue="// some comment" />;
}

const rootElement = document.getElementById('root');
ReactDOM.render(<App />, rootElement);

codesandbox

Extended example
import React from 'react';
import ReactDOM from 'react-dom';

import Editor from '@monaco-editor/react';

function App() {
  function handleEditorChange(value, event) {
    // here is the current value
  }

  function handleEditorDidMount(editor, monaco) {
    console.log('onMount: the editor instance:', editor);
    console.log('onMount: the monaco instance:', monaco);
  }

  function handleEditorWillMount(monaco) {
    console.log('beforeMount: the monaco instance:', monaco);
  }

  function handleEditorValidation(markers) {
    // model markers
    // markers.forEach(marker => console.log('onValidate:', marker.message));
  }

  return (
    <Editor
      height="90vh"
      defaultLanguage="javascript"
      defaultValue="// some comment"
      onChange={handleEditorChange}
      onMount={handleEditorDidMount}
      beforeMount={handleEditorWillMount}
      onValidate={handleEditorValidation}
    />
  );
}

const rootElement = document.getElementById('root');
ReactDOM.render(<App />, rootElement);

codesandbox

Get value

There are two options to get the current value:

  1. get the current model value from the editor instance
import React, { useRef } from 'react';
import ReactDOM from 'react-dom';

import Editor from '@monaco-editor/react';

function App() {
  const editorRef = useRef(null);

  function handleEditorDidMount(editor, monaco) {
    editorRef.current = editor;
  }

  function showValue() {
    alert(editorRef.current.getValue());
  }

  return (
    <>
      <button onClick={showValue}>Show value</button>
      <Editor
        height="90vh"
        defaultLanguage="javascript"
        defaultValue="// some comment"
        onMount={handleEditorDidMount}
      />
    </>
  );
}

const rootElement = document.getElementById('root');
ReactDOM.render(<App />, rootElement);

codesandbox

  1. get the current model value via onChange prop
import React from 'react';
import ReactDOM from 'react-dom';

import Editor from '@monaco-editor/react';

function App() {
  function handleEditorChange(value, event) {
    console.log('here is the current model value:', value);
  }

  return (
    <Editor
      height="90vh"
      defaultLanguage="javascript"
      defaultValue="// some comment"
      onChange={handleEditorChange}
    />
  );
}

const rootElement = document.getElementById('root');
ReactDOM.render(<App />, rootElement);

codesandbox

(get the `DiffEditor` values via `editor` instance)
import React, { useRef } from 'react';
import ReactDOM from 'react-dom';

import { DiffEditor } from '@monaco-editor/react';

function App() {
  const diffEditorRef = useRef(null);

  function handleEditorDidMount(editor, monaco) {
    diffEditorRef.current = editor;
  }

  function showOriginalValue() {
    alert(diffEditorRef.current.getOriginalEditor().getValue());
  }

  function showModifiedValue() {
    alert(diffEditorRef.current.getModifiedEditor().getValue());
  }

  return (
    <>
      <button onClick={showOriginalValue}>show original value</button>
      <button onClick={showModifiedValue}>show modified value</button>
      <DiffEditor
        height="90vh"
        language="javascript"
        original="// the original code"
        modified="// the modified code"
        onMount={handleEditorDidMount}
      />
    </>
  );
}

const rootElement = document.getElementById('root');
ReactDOM.render(<App />, rootElement);

codesandbox

editor instance

The editor instance is exposed from the onMount prop as a first parameter, the second is the monaco instance

import React, { useRef } from 'react';
import ReactDOM from 'react-dom';

import Editor from '@monaco-editor/react';

function App() {
  const editorRef = useRef(null);

  function handleEditorDidMount(editor, monaco) {
    // here is the editor instance
    // you can store it in `useRef` for further usage
    editorRef.current = editor;
  }

  return (
    <Editor
      height="90vh"
      defaultLanguage="javascript"
      defaultValue="// some comment"
      onMount={handleEditorDidMount}
    />
  );
}

const rootElement = document.getElementById('root');
ReactDOM.render(<App />, rootElement);

codesandbox

monaco instance

There are three options to get the monaco instance:

  1. via onMount/beforeMount
import React, { useRef } from 'react';
import ReactDOM from 'react-dom';

import Editor from '@monaco-editor/react';

function App() {
  const monacoRef = useRef(null);

  function handleEditorWillMount(monaco) {
    // here is the monaco instance
    // do something before editor is mounted
    monaco.languages.typescript.javascriptDefaults.setEagerModelSync(true);
  }

  function handleEditorDidMount(editor, monaco) {
    // here is another way to get monaco instance
    // you can also store it in `useRef` for further usage
    monacoRef.current = monaco;
  }

  return (
    <Editor
      height="90vh"
      defaultLanguage="javascript"
      defaultValue="// some comment"
      beforeMount={handleEditorWillMount}
      onMount={handleEditorDidMount}
    />
  );
}

const rootElement = document.getElementById('root');
ReactDOM.render(<App />, rootElement);

codesandbox

  1. via loader utility
import { loader } from '@monaco-editor/react';

loader.init().then((monaco) => console.log('here is the monaco instance:', monaco));

codesandbox

  1. via useMonaco hook
import React from 'react';
import ReactDOM from 'react-dom';

import Editor, { useMonaco } from '@monaco-editor/react';

function App() {
  const monaco = useMonaco();

  useEffect(() => {
    if (monaco) {
      console.log('here is the monaco instance:', monaco);
    }
  }, [monaco]);

  return <Editor height="90vh" defaultValue="// some comment" defaultLanguage="javascript" />;
}

const rootElement = document.getElementById('root');
ReactDOM.render(<App />, rootElement);

codesandbox

useMonaco

useMonaco is a React hook that returns the instance of the monaco. But there is an important note that should be considered: the initialization process is being handled by the loader utility (the reference of @monaco-editor/loader): that process is being done asynchronously and only once. So, if the first initiator of the initialization is useMonaco hook, the first returned value will be null, due to its asynchronous installation. Just check the returned value of useMonaco

import React, { useEffect } from 'react';
import ReactDOM from 'react-dom';

import Editor, { useMonaco } from '@monaco-editor/react';

function App() {
  const monaco = useMonaco();

  useEffect(() => {
    // do conditional chaining
    monaco?.languages.typescript.javascriptDefaults.setEagerModelSync(true);
    // or make sure that it exists by other ways
    if (monaco) {
      console.log('here is the monaco instance:', monaco);
    }
  }, [monaco]);

  return <Editor height="90vh" defaultValue="// some comment" defaultLanguage="javascript" />;
}

const rootElement = document.getElementById('root');
ReactDOM.render(<App />, rootElement);

codesandbox

loader-config

The library exports (named) the utility called loader. Basically, it's the reference of @monaco-editor/loader. By default, monaco files are being downloaded from CDN. There is an ability to change this behavior, and other things concerning the AMD loader of monaco. We have a default config file that you can modify by the way shown below:

import { loader } from '@monaco-editor/react';

// you can change the source of the monaco files
loader.config({ paths: { vs: '...' } });

// you can configure the locales
loader.config({ 'vs/nls': { availableLanguages: { '*': 'de' } } });

// or
loader.config({
  paths: {
    vs: '...',
  },
  'vs/nls': {
    availableLanguages: {
      '*': 'de',
    },
  },
});
use monaco-editor as an npm package

Starting from version v4.4.0 it's possible to use monaco-editor as an npm package; import it from node_modules and include monaco sources into your bundle (instead of using CDN). To make it work you can do the following:

import * as monaco from 'monaco-editor';
import { loader } from '@monaco-editor/react';

loader.config({ monaco });

// ...

NOTE: you should be aware that this may require additional webpack plugins, like monaco-editor-webpack-plugin or it may be impossible to use in apps generated by CRA without ejecting them.

If you use Vite, you need to do this:

import { loader } from '@monaco-editor/react';

import * as monaco from 'monaco-editor';
import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker';
import jsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker';
import cssWorker from 'monaco-editor/esm/vs/language/css/css.worker?worker';
import htmlWorker from 'monaco-editor/esm/vs/language/html/html.worker?worker';
import tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker';

self.MonacoEnvironment = {
  getWorker(_, label) {
    if (label === 'json') {
      return new jsonWorker();
    }
    if (label === 'css' || label === 'scss' || label === 'less') {
      return new cssWorker();
    }
    if (label === 'html' || label === 'handlebars' || label === 'razor') {
      return new htmlWorker();
    }
    if (label === 'typescript' || label === 'javascript') {
      return new tsWorker();
    }
    return new editorWorker();
  },
};

loader.config({ monaco });

loader.init().then(/* ... */);

codesandbox

NOTE: your passed object will be deeply merged with the default one

Multi-model editor

When you render the Editor component, a default model is being created. It's important to mention that when you change the language or value props, they affect the same model that has been auto-created at the mount of the component. In most cases it's okay, but the developers face problems when they want to implement a multi-model editor to support tabs/files like in IDEs. And previously to handle multiple models they had to do it manually and out of the component. Now, the multi-model API is supported 🎉 Let's check how it works. There are three parameters to create a model - value, language and path (monaco.editor.createModel(value, language, monaco.Uri.parse(path))). You can consider last one (path) as an identifier for the model. The Editor component, now, has a path prop. When you specify a path prop, the Editor component checks if it has a model by that path or not. If yes, the existing model will be shown, otherwise, a new one will be created (and stored). Using this technique you can correspond your files with paths, and create a fully multi-model editor. You can open your file, do some changes, choose another file, and when you come back to the first one the previous model will be shown with the whole view state, text selection, undo stack, scroll position, etc. (simple demo)

Here is a simple example: let's imagine we have a JSON like representation of some file structure, something like this:

const files = {
  'script.js': {
    name: 'script.js',
    language: 'javascript',
    value: someJSCodeExample,
  },
  'style.css': {
    name: 'style.css',
    language: 'css',
    value: someCSSCodeExample,
  },
  'index.html': {
    name: 'index.html',
    language: 'html',
    value: someHTMLCodeExample,
  },
};

And here is our simple multi-model editor implementation:

import React from 'react';
import ReactDOM from 'react-dom';

import Editor from '@monaco-editor/react';

function App() {
  const [fileName, setFileName] = useState('script.js');

  const file = files[fileName];

  return (
    <>
      <button disabled={fileName === 'script.js'} onClick={() => setFileName('script.js')}>
        script.js
      </button>
      <button disabled={fileName === 'style.css'} onClick={() => setFileName('style.css')}>
        style.css
      </button>
      <button disabled={fileName === 'index.html'} onClick={() => setFileName('index.html')}>
        index.html
      </button>
      <Editor
        height="80vh"
        theme="vs-dark"
        path={file.name}
        defaultLanguage={file.language}
        defaultValue={file.value}
      />
    </>
  );
}

const rootElement = document.getElementById('root');
ReactDOM.render(<App />, rootElement);

The properties:

  • defaultValue
  • defaultLanguage
  • defaultPath
  • value
  • language
  • path
  • saveViewState

will give you more flexibility in working with a multi-model editor.

NOTE

defaultValue, defaultLanguage, and defaultPath are being considered only during a new model creation
value, language, and path are being tracked the whole time
saveViewState is an indicator whether to save the models' view states between model changes or not

codesandbox

onValidate

onValidate is an additional property. An event is emitted when the content of the current model is changed and the current model markers are ready. It will be fired with the current model markers

import React from 'react';
import ReactDOM from 'react-dom';

import Editor from '@monaco-editor/react';

function App() {
  function handleEditorValidation(markers) {
    // model markers
    markers.forEach((marker) => console.log('onValidate:', marker.message));
  }

  return (
    <Editor
      height="90vh"
      defaultLanguage="javascript"
      defaultValue="// let's write some broken code 😈"
      onValidate={handleEditorValidation}
    />
  );
}

const rootElement = document.getElementById('root');
ReactDOM.render(<App />, rootElement);

codesandbox

It's important to mention that according to monaco-editor, the whole supported languages are divided into two groups:

  1. languages that have rich IntelliSense and validation
  • TypeScript
  • JavaScript
  • CSS
  • LESS
  • SCSS
  • JSON
  • HTML
  1. languages with only basic syntax colorization
  • XML
  • PHP
  • C#
  • C++
  • Razor
  • Markdown
  • Diff
  • Java
  • VB
  • CoffeeScript
  • Handlebars
  • Batch
  • Pug
  • F#
  • Lua
  • Powershell
  • Python
  • Ruby
  • SASS
  • R
  • Objective-C

As you can guess, onValidate prop will work only with the languages from the first group

Notes

For electron users

As a usual React component, this one also works fine with an electron-react environment, without need to have a webpack configuration or other extra things. But there are several cases that developers usually face to and sometimes it can be confusing. Here they are:

  1. You see loading screen stuck Usually, it's because your environment doesn't allow you to load external sources. By default, it loads monaco sources from CDN. You can see the default configuration. But sure you can change that behavior; the library is fully configurable. Read about it here. So, if you want to download it from your local files, you can do it like this:
import { loader } from '@monaco-editor/react';

loader.config({ paths: { vs: '../path-to-monaco' } });
  1. Based on your electron environment it can be required to have an absolute URL The utility function taken from here can help you to achieve that. Let's imagine you have monaco-editor package installed and you want to load monaco from the node_modules rather than from CDN: in that case, you can write something like this:
function ensureFirstBackSlash(str) {
  return str.length > 0 && str.charAt(0) !== '/' ? '/' + str : str;
}

function uriFromPath(_path) {
  const pathName = path.resolve(_path).replace(/\\/g, '/');
  return encodeURI('file://' + ensureFirstBackSlash(pathName));
}

loader.config({
  paths: {
    vs: uriFromPath(path.join(__dirname, '../node_modules/monaco-editor/min/vs')),
  },
});

There were several issues about this topic that can be helpful too - 1 2 3 4

And if you use electron with monaco and react and have faced an issue different than the above-discribed ones, please let us know to make this section more helpful

For Next.js users

Like other React components, this one also works with Next.js without a hitch. The part of the source that should be pre-parsed is optimized for server-side rendering, so, in usual cases, it will work fine, but if you want to have access, for example, to monaco instance you should be aware that it wants to access the document object, and it requires browser environment. Basically you just need to avoid running that part out of browser environment, there are several ways to do that. The one is described here

And if you use monaco with Next.js and have faced an issue different than the above-described one, please let us know to make this section more helpful

Create your own editor

Under the hood this library uses @monaco-editor/loader that provides a utility called loader. The loader utility is a collection of functions that are being used to setup monaco editor into your browser. loader.init() handles the whole initialization process and returns the instance of the monaco - loader.init().then(monaco => console.log("here is the monaco instance:", monaco)). The Editor component uses this utility, gains access to monaco instance and creates the editor. Here is the implementation of the Editor component. You can use the same technique to create your own Editor. You can just import the loader utility, access to monaco instance, and create your own editor with your own custom logic. The shortest way to do it:

import loader from '@monaco-editor/loader';

loader.init().then((monaco) => {
  const wrapper = document.getElementById('root');
  wrapper.style.height = '100vh';
  const properties = {
    value: 'function hello() {\n\talert("Hello world!");\n}',
    language: 'javascript',
  };

  monaco.editor.create(wrapper, properties);
});

That's all. You can wrap it into a React component, or Vue, or Angular or leave it as vanilla one or whatever you want; it's written in pure js

codesandbox

Development-Playground

It's always important to have a place, where you can play with the internals of the library. The playground is a minimal React app that directly uses the sources of the library. So, if you are going to open a PR, or want to check something, or just want to try the freshest state of the library, you can run the playground and enjoy it

  • clone the repository
git clone https://github.com/suren-atoyan/monaco-react.git
  • go to the library folder
 cd monaco-react
  • install the library's dependencies
 npm install # yarn
  • go to the playground
 cd playground
  • install the playground's dependencies
npm install # yarn
  • and run the playground
npm run dev # yarn dev
monaco-react
├── playground
│   ├── src/      # playground sources
├── src/          # library sources
└── ...

If you want to change something in the library, go to monaco-react/src/..., the library will be automatically re-built and the playground will use the latest build

Props

Editor

Name Type Default Description
defaultValue string Default value of the current model
defaultLanguage string Default language of the current model
defaultPath string Default path of the current model. Will be passed as the third argument to .createModel method - monaco.editor.createModel(..., ..., monaco.Uri.parse(defaultPath))
value string Value of the current model
language enum: ... Language of the current model (all languages that are supported by monaco-editor)
path string Path of the current model. Will be passed as the third argument to .createModel method - monaco.editor.createModel(..., ..., monaco.Uri.parse(defaultPath))
theme enum: "light" | "vs-dark" "light" The theme for the monaco. Available options "vs-dark" | "light". Define new themes by monaco.editor.defineTheme
line number The line to jump on it
loading React Node "Loading..." The loading screen before the editor will be mounted
options object {} IStandaloneEditorConstructionOptions
overrideServices object {} IEditorOverrideServices
saveViewState boolean true Indicator whether to save the models' view states between model changes or not
keepCurrentModel boolean false Indicator whether to dispose the current model when the Editor is unmounted or not
width union: number | string "100%" Width of the editor wrapper
height union: number | string "100%" Height of the editor wrapper
className string Class name for the editor container
wrapperProps object {} Props applied to the wrapper element
beforeMount func noop Signature: function(monaco: Monaco) => void
An event is emitted before the editor is mounted. It gets the monaco instance as a first argument
onMount func noop Signature: function(editor: monaco.editor.IStandaloneCodeEditor, monaco: Monaco) => void
An event is emitted when the editor is mounted. It gets the editor instance as a first argument and the monaco instance as a second
onChange func Signature: function(value: string | undefined, ev: monaco.editor.IModelContentChangedEvent) => void
An event is emitted when the content of the current model is changed
onValidate func noop Signature: function(markers: monaco.editor.IMarker[]) => void
An event is emitted when the content of the current model is changed and the current model markers are ready

DiffEditor

Name Type Default Description
original string The original source (left one) value
modified string The modified source (right one) value
language enum: ... Language for the both models - original and modified (all languages that are supported by monaco-editor)
originalLanguage enum: ... This prop gives you the opportunity to specify the language of the original source separately, otherwise, it will get the value of the language property
modifiedLanguage enum: ... This prop gives you the opportunity to specify the language of the modified source separately, otherwise, it will get the value of language property
originalModelPath string Path for the "original" model. Will be passed as a third argument to .createModel method - monaco.editor.createModel(..., ..., monaco.Uri.parse(originalModelPath))
modifiedModelPath string Path for the "modified" model. Will be passed as a third argument to .createModel method - monaco.editor.createModel(..., ..., monaco.Uri.parse(modifiedModelPath))
keepCurrentOriginalModel boolean false Indicator whether to dispose the current original model when the DiffEditor is unmounted or not
keepCurrentModifiedModel boolean false Indicator whether to dispose the current modified model when the DiffEditor is unmounted or not
theme enum: "light" | "vs-dark" "light" The theme for the monaco. Available options "vs-dark" | "light". Define new themes by monaco.editor.defineTheme
line number The line to jump on it
loading React Node "Loading..." The loading screen before the editor will be mounted
options object {} IDiffEditorConstructionOptions
width union: number | string "100%" Width of the editor wrapper
height union: number | string "100%" Height of the editor wrapper
className string Class name for the editor container
wrapperProps object {} Props applied to the wrapper element
beforeMount func noop Signature: function(monaco: Monaco) => void
An event is emitted before the editor mounted. It gets the monaco instance as a first argument
onMount func noop Signature: function(editor: monaco.editor.IStandaloneCodeEditor, monaco: Monaco) => void
An event is emitted when the editor is mounted. It gets the editor instance as a first argument and the monaco instance as a second

License

MIT

monaco-react's People

Contributors

a-sam-randall avatar amcdnl avatar brianbancroft avatar chaseholdren avatar cobertos avatar danibram avatar dehoward avatar dreamacro avatar fengzilong avatar hacker0limbo avatar hogpilot avatar jastor11 avatar jerebtw avatar jtang0506 avatar juliusmarminge avatar kaleocheng avatar koresamuel avatar luhis avatar lywtyh avatar m7yue avatar nealchandra avatar nurou avatar oskardamkjaer avatar psrok1 avatar shubham-p-patil avatar siebeve avatar spiderpig86 avatar stefandbtlabs avatar suren-atoyan avatar xyy94813 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

monaco-react's Issues

Can't replicate "basic example" from Monaco Playground

In the basic example on the Monaco Editor Playground, it is possible to set options like lineNumbers and scrollBeyondLastLine like this:

// Through the options literal, the behaviour of the editor can be easily customized.
// Here are a few examples of config options that can be passed to the editor.
// You can also call editor.updateOptions at any time to change the options.

var editor = monaco.editor.create(document.getElementById("container"), {
	value: "// First line\nfunction hello() {\n\talert('Hello world!');\n}\n// Last line",
	language: "javascript",

	lineNumbers: "off",                 // in particular options like these
	roundedSelection: false,
	scrollBeyondLastLine: true,
	readOnly: true,
	theme: "vs-dark",
});

As far as I can tell it is not possible to do this with monaco.config(), monaco.init().then(...) or through useRef and editorDidMount. How can I go about providing these configuration options to the editor?

Error with value change before initialization

Hi,
thanks a lot for your work!

Most things are working fine for me and it is nice to get rid of the external loader.

I realized, that your script crashes, if there are any changes (e.g. of the value) before the editor is initialized. At the moment, I check if the ref is set before updating the value, otherwise I get:
Uncaught TypeError: Cannot read property 'setValue' of undefined
The same happens at least to the options and language props.

It looks to me like the useUpdate hook should check that but somehow does not work like intended?

CodeSandbox with the error:
https://codesandbox.io/embed/monaco-editor-react-4bvf0
If check for the mounted editor on language and value it will work (l. 46 to l. 50).

Error: Unexpected usage / You must define a function MonacoEnvironment.getWorkerUrl or MonacoEnvironment.getWorker

Hello!
I don't know if I am doing something wrong but when I use the Editor component I get strange errors that I didn't have with the webpack plugin for monaco editor.

The error happens when I use monaco editor's variables and your Editor at the same time.

Please try this simple example so you can understand better what i mean.

In this example I just use a KeyCode enum from monaco-editor and your component generate errors.

Thank you for your help!

PS : may be related to this issue

Request : onBlur for Controlled Editor

Can you add and onBlur function for Controlled editor or please direct me in direction where I can do so. Its an important side effect for any controlled component.

TypeError: cancelable.cancel is not a function

Describe the bug
I randomly get this error crashing my application.

To Reproduce

Steps to reproduce the behavior:
I don't know how to reproduce this because it sometimes happens and sometimes does not.
It happens immediately when the route in which I load the monaco editor is navigated to.

Expected behavior
I expected it not to crash.
Setting breakpoints to pause on exception does not pause anywhere.
Perhaps by setting break points at all (before monaco loads) I allow it to wait and mount, causing cancellable.cancel to be a defined function.

Screenshots
screencapture-localhost-3000-candidate-assessment-locald28cf7ce96cc4697aeb37d2b72612120-1582857075271

Desktop (please complete the following information):

  • OS: Both Windows 10 and MacOs 10.15
  • Browser: Chrome
  • Version: 80

Awesome project!

Just wanted to say thanks for this project!

I'm using mdx-deck (which uses Gatsby) and I had all sort of issues with the other version of the Monaco Editor for React. Once I switched to your project it worked out of the box like a charm!

Just close the issue once you read it, only wanted to say thanks!

Example code for DiffEditor?

Is your feature request related to a problem? Please describe.
Not able to find the example code for the DiffEditor. Just like simple usage[...]

Describe the solution you'd like
A clear and concise description of what you want to happen.

Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.

Additional context
import DiffEditor from '@monaco-editor/react';
<DiffEditor original="rest" modified="rewe" height="90vh" language="json" />

doing this process doesn't show up the DiffEditor.

[Feature] Auto formating

When the component loads with content it does not auto-format it, could you please explain how to do it or give access to editor.getAction('editor.action.format') methods.

Global window.monaco object.

It's useful to get a hold of the window.monaco object for language support, for example. However, the window.monaco doesn't get injected, I presume, until an instance of a MonacoEditor is created.

Would it be useful (or possible?) to break out that injection into a call that my app could load on startup, so that I can begin pre-registering languages, for instance?

Currently, I wait until an editorDidMount() call before attempting to register language signatures and autocompletes.

Thanks again!

Can't resolve '@monaco-editor/react'

I'm running a react app with docker-compose and I keep getting this error:
Can't resolve '@monaco-editor/react'

Here is my docker-compose:

  web:
    container_name: web
    build:
      context: ./web
      dockerfile: Dockerfile.dev
    volumes:
      - ./web/app:/app
      - /app/node_modules
    ports:
      - '8080:3000'
    environment:
      - CHOKIDAR_USEPOLLING=true
      - API_HOST=api
      - API_PORT=3000
      - COMPOSE_HTTP_TIMEOUT=200
    command: npm start

Here is my Dockerfile:

FROM node:alpine

RUN apk update
RUN apk upgrade
RUN command apk add bash

WORKDIR /app

# add `/app/node_modules/.bin` to $PATH
ENV PATH /app/node_modules/.bin:$PATH

#COPY ./app .

# install and cache app dependencies
COPY ./app/package.json /app/package.json
RUN npm install --silent
RUN npm install react-scripts -g --silent

# start app
CMD ["npm", "start"]

I verified that folder "@monaco-editor" is present in node_modules folder.

Any idea about what's wrong?

ControlledEditor event not fired

Hi surren
i am using this component but onchange event not fired

import React from "react";
import ControlledEditor from "@monaco-editor/react";

export default function MonacoEditor(props) {
	const handleEditorChange = (ev, value) => {
		console.log('onChange', value);
	};

	return (
		<ControlledEditor
			height="90vh" 
			theme={"dark"}
			language={props.language || 'javascript'}
			value={props.value}
			onChange={handleEditorChange}
		/>
	);
}

ControlledEditor doesn't update when value changes in readOnly mode

Hi, thanks for making this package! The lack of webpack configuration is very nice. 😄

I'm trying to use MonacoEditor in readOnly: true mode as an output visualizer for some text transforms. When the value provided to the editor is updated, the editor's contents don't change. It's quite unfortunate.

The issue can be kind of worked around by providing key={value}, but that causes the editor to refresh all its DOM elements every update. Rather unfortunate.

Edit: here's a reproducible sample: budgielang/Budgie-Web-Client@cd7f0c7#diff-caa2478e3f773087d1ce9d097daf1d16R35.

Edit 2: interestingly, this bug goes away when readOnly: true is not provided.

React state update on unmounted component

If you quickly render and kill a component with monaco-react in it the following error happens :

backend.js:6 Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.
    in Editor
    in Editor (created by SizedDiv)
    in div (created by SizedDiv)
    in SizedDiv (created by SrcEditor)
    in SrcEditor (created by WegasScriptEditor)
    in WegasScriptEditor (created by ExpressionEditor)
    in div (created by ExpressionEditor)
    in div (created by ExpressionEditor)
    in ExpressionEditor (created by Labeled)
    in Labeled (created by StatementView)
    in div (created by CommonViewContainer)
    in CommonViewContainer (created by StatementView)
    in StatementView (created by j)
    in j (created by r)
    in r (created by n)
    in n (created by Context.Consumer)
    in Unknown (created by r)
    in r (created by Context.Consumer)
    in Unknown (created by n)
    in n (created by n)
    in n (created by r)
    in fieldset (created by ObjectView)
    in div (created by CommonViewContainer)
    in CommonViewContainer (created by ObjectView)
    in ObjectView (created by j)
    in j (created by r)
    in r (created by n)
    in n (created by Context.Consumer)
    in Unknown (created by r)
    in r (created by Context.Consumer)
    in Unknown (created by n)
    in n (created by n)
    in n
    in div (created by ArrayItem)
    in div (created by ArrayItem)
    in ArrayItem (created by Labeled)
r @ backend.js:6
warningWithoutStack @ react-dom.development.js:545
warnAboutUpdateOnUnmountedFiberInDEV @ react-dom.development.js:23177
scheduleUpdateOnFiber @ react-dom.development.js:21394
dispatchAction @ react-dom.development.js:15816
(anonymous) @ Editor.js:67
Promise.then (async)
(anonymous) @ Editor.js:66
commitHookEffectList @ react-dom.development.js:19986
commitPassiveHookEffects @ react-dom.development.js:20016
callCallback @ react-dom.development.js:347
invokeGuardedCallbackDev @ react-dom.development.js:397
invokeGuardedCallback @ react-dom.development.js:454
flushPassiveEffectsImpl @ react-dom.development.js:22868
unstable_runWithPriority @ scheduler.development.js:643
runWithPriority$2 @ react-dom.development.js:11305
flushPassiveEffects @ react-dom.development.js:22841
renderRoot @ react-dom.development.js:21801
runRootCallback @ react-dom.development.js:21554
(anonymous) @ react-dom.development.js:11353
unstable_runWithPriority @ scheduler.development.js:643
runWithPriority$2 @ react-dom.development.js:11305
flushSyncCallbackQueueImpl @ react-dom.development.js:11349
flushSyncCallbackQueue @ react-dom.development.js:11338
discreteUpdates$1 @ react-dom.development.js:21677
discreteUpdates @ react-dom.development.js:2359
dispatchDiscreteEvent @ react-dom.development.js:5979

The same error is happening with the diffEditor.

I'll soon make a pull request to correct this.

access editor variable from component using monaco-react

Hi @SurenAt93,

Thanks for making a great library!

I'm hoping use some of Monaco's built in extensions like those found here from a create-react-app.

Use case: I would like to add margin glyphs with click handlers to communicate with other app components. I'm imagining a fair amount of other decorations that would be nice to customize.

These all use the same API as when you monacoRef.current.editor...

As the monacoRef is private, is there any way this could be exposed in order that such logic decoration behavior can live in parent components?

Happy to submit a PR!

Editor do not respect grid when resizing

Inspect this pin.

If you are having your Editor in a grid-parent, it would scale up properly, but it won't scale down.
It looks like the editors width and height are hardcoded into "monaco-editor" div styles.

exmpl

And they do not take into account the surrounding elements when resizing.

To fix this example you need to wrap the whole editor into div with overflow: hidden; style.

how to access transpileModule

Hi,

Is it possible to get access to transpileModule method?
When using vanilla JS and require you can access it using global ts variable:

var js = ts.transpileModule(editor.getModel().getValue(), { compilerOptions: { module: ts.ModuleKind.None } });

thanks!

Scrolling does not work in Firefox

Hello,
Right now there is a known bug with Firefox in Monaco editor. Scrolling is not working in Firefox. Issue details are here.

microsoft/monaco-editor#1353

They have added it to master but new version is not released yet. I was wondering if you can provide some patch in meantime until the solution is published in new version.

ControlledEditor doesn't fire onChange upon Undo/Redo

Undo and redo typically change the contents of a ControlledEditor but onChange doesn't fire. I see the code has an explicit check that prevents this which was added in 2.3.0, but it's not clear why. Undo and redo can change the content of the editor just like any other operation, so onChange should fire.

Cannot read property 'dispose' of undefined.

Not an issue.

The above error occur at Editor.js

var removeEditor = function removeEditor(_) { return editorRef.current.dispose(); };
I am using Controlled Editor.
Its working fine in few of my screens/view.

ts build not work

react-dom.production.min.js:4408 TypeError: (intermediate value)(intermediate value)(intermediate value).create is not a function
at N (textModel.ts:50)
at new r (textModel.ts:206)
at t._createModelData (modelServiceImpl.ts:255)
at t.createModel (modelServiceImpl.ts:277)
at P (standaloneEditor.ts:144)
at Object.A [as createModel] (standaloneEditor.ts:165)
at new t (standaloneCodeEditor.ts:317)
at standaloneEditor.ts:73
at M (standaloneEditor.ts:57)
at Object.k [as create] (standaloneEditor.ts:72)
hi @ react-dom.production.min.js:4408
Mi.n.callback @ react-dom.production.min.js:4773
ri @ react-dom.production.min.js:4271
ci @ react-dom.production.min.js:4257
Ui @ react-dom.production.min.js:4999
Bi @ react-dom.production.min.js:5123
(anonymous) @ react-dom.production.min.js:5975
t.unstable_runWithPriority @ scheduler.production.min.js:274
Ea @ react-dom.production.min.js:5974
xa @ react-dom.production.min.js:5958
Ta @ react-dom.production.min.js:5925
Ca @ react-dom.production.min.js:5860
(anonymous) @ react-dom.production.min.js:5061
Wi @ react-dom.production.min.js:5067
enqueueSetState @ react-dom.production.min.js:2788
M.setState @ react.production.min.js:72
(anonymous) @ index.tsx:118
Promise.then (async)
a.handleLabel @ index.tsx:94
value @ index.tsx:78
sr @ react-dom.production.min.js:2854
Lo @ react-dom.production.min.js:3759
_o @ react-dom.production.min.js:3960
qi @ react-dom.production.min.js:5514
Yi @ react-dom.production.min.js:5536
xa @ react-dom.production.min.js:5958
Ta @ react-dom.production.min.js:5925
Ca @ react-dom.production.min.js:5860
Zi @ react-dom.production.min.js:5787
enqueueSetState @ react-dom.production.min.js:2790
M.setState @ react.production.min.js:72
t @ index.js:240
(anonymous) @ index.js:250
Promise.then (async)
n._loadModule @ index.js:249
n.componentWillMount @ index.js:197
sr @ react-dom.production.min.js:2854
Lo @ react-dom.production.min.js:3759
_o @ react-dom.production.min.js:3960
qi @ react-dom.production.min.js:5514
Yi @ react-dom.production.min.js:5536
xa @ react-dom.production.min.js:5958
Ta @ react-dom.production.min.js:5925
Aa @ react-dom.production.min.js:6020
Sn @ react-dom.production.min.js:1737
react-dom.production.min.js:4408 TypeError: Cannot read property 'dispose' of undefined
at k (Editor.js:210)
at di (react-dom.production.min.js:4498)
at bi (react-dom.production.min.js:4655)
at Fi (react-dom.production.min.js:4914)
at Bi (react-dom.production.min.js:5104)
at react-dom.production.min.js:5975
at Object.t.unstable_runWithPriority (scheduler.production.min.js:274)
at Ea (react-dom.production.min.js:5974)
at xa (react-dom.production.min.js:5958)
at Ta (react-dom.production.min.js:5925)
hi @ react-dom.production.min.js:4408
Mi.n.callback @ react-dom.production.min.js:4773
ri @ react-dom.production.min.js:4271
ci @ react-dom.production.min.js:4257
Ui @ react-dom.production.min.js:4999
Bi @ react-dom.production.min.js:5123
(anonymous) @ react-dom.production.min.js:5975
t.unstable_runWithPriority @ scheduler.production.min.js:274
Ea @ react-dom.production.min.js:5974
xa @ react-dom.production.min.js:5958
Ta @ react-dom.production.min.js:5925
Ca @ react-dom.production.min.js:5860
(anonymous) @ react-dom.production.min.js:5061
Wi @ react-dom.production.min.js:5067
enqueueSetState @ react-dom.production.min.js:2788
M.setState @ react.production.min.js:72
(anonymous) @ index.tsx:118
Promise.then (async)
a.handleLabel @ index.tsx:94
value @ index.tsx:78
sr @ react-dom.production.min.js:2854
Lo @ react-dom.production.min.js:3759
_o @ react-dom.production.min.js:3960
qi @ react-dom.production.min.js:5514
Yi @ react-dom.production.min.js:5536
xa @ react-dom.production.min.js:5958
Ta @ react-dom.production.min.js:5925
Ca @ react-dom.production.min.js:5860
Zi @ react-dom.production.min.js:5787
enqueueSetState @ react-dom.production.min.js:2790
M.setState @ react.production.min.js:72
t @ index.js:240
(anonymous) @ index.js:250
Promise.then (async)
n._loadModule @ index.js:249
n.componentWillMount @ index.js:197
sr @ react-dom.production.min.js:2854
Lo @ react-dom.production.min.js:3759
_o @ react-dom.production.min.js:3960
qi @ react-dom.production.min.js:5514
Yi @ react-dom.production.min.js:5536
xa @ react-dom.production.min.js:5958
Ta @ react-dom.production.min.js:5925
Aa @ react-dom.production.min.js:6020
Sn @ react-dom.production.min.js:1737
textModel.ts:50 Uncaught (in promise) TypeError: (intermediate value)(intermediate value)(intermediate value).create is not a function
at N (textModel.ts:50)
at new r (textModel.ts:206)
at t._createModelData (modelServiceImpl.ts:255)
at t.createModel (modelServiceImpl.ts:277)
at P (standaloneEditor.ts:144)
at Object.A [as createModel] (standaloneEditor.ts:165)
at new t (standaloneCodeEditor.ts:317)
at standaloneEditor.ts:73
at M (standaloneEditor.ts:57)
at Object.k [as create] (standaloneEditor.ts:72)

TypeError: monaco config error

please add monaca config type.

declare namespace monaco {
  function init(): Promise<Monaco>;
}

=>

declare namespace monaco {
  function init(): Promise<Monaco>;
  function config(params:{urls:{monacoLoader?:string,monacoBase?:string}} ):void;
}

SCSS syntax is working

Please can you describe how to load comparability with things like scss? It doesn't work and is apparently part of the monaco-editor library by default

TypeError: Cannot read property 'getModeId' of null

Hello!

I often get this error from the editor when i use my own schema.

I used to dispose models before creating a new editor.
Can it be related with this?
Should I dispose models and editor or you're allready doing that in your Editor component?

I found out a related issue but it seem that the problem has been corrected long ago.

image

Support Offline

Hello,
We are using this package in Cayley UI which is served locally and should work offline. But, I can see the code tries to access online URLs. Can you support offline use?

Syntax Highlighting with Gatsby

The react component works perfectly in my Create React App, but it will not work with Gatsby which I am using for HMR. Does Monaco not work with HMR related things?

ControlledEditor not remove css color properties

Hi,
Thank you for sharing your work, good work & amazing;)

I have a problem in the ControlledEditor component, if I change the language / mode dynamically, there is a problem in the css language. The problem is the css property that has colors with hexa, rgba or any color mode in css doesn't remove the preview color if I change to another language.

This is an example:

2019-10-07_20-47-31

I hope You can give some guide.

Thanks :)

Out of Memory Error

I updated to v2.3.0 and now I get out of memory errors when i try to edit C#.

Example page:
https://codegolf.club/demo

just typing in random text in the Monaco editor will cause the out of memory error to happen.

TypeError: Cannot read property 'dispose' of undefined

Hi, I've been playing around with your React integration of Monaco. Thanks for all your hard work in doing this!

I ran into a few problems when the editor unmounts. I'm creating the editor within a functional component and sometimes when I click something that would cause the editor to unmount (I presume), I get errors such as:

  TypeError: Cannot read property 'updateOptions' of undefined
(anonymous function)
src/afw_app/admin/node_modules/@monaco-editor/react/lib/Editor/Editor.js:166
  163 |   monacoRef.current.editor.setTheme(theme);
  164 | }, [theme]);
  165 | (0, _hooks.useUpdate)(function (_) {
> 166 |   editorRef.current.updateOptions(options);
      | ^  167 | }, [options]);
  168 | 
  169 | function createEditor() {

And:

�TypeError: Cannot read property 'dispose' of undefined
removeEditor
src/afw_app/admin/node_modules/@monaco-editor/react/lib/Editor/Editor.js:182
  179 | }
  180 | 
  181 | function removeEditor() {
> 182 |   editorRef.current.dispose();
      | ^  183 | }
  184 | 
  185 | return _react["default"].createElement("section", {

I have a few other questions/comments, that I'll leave in other open issues.

Language Validation

Thank you for making this, it has been really helpful!

The monaco editor currently has some language validation built in for HTML, CSS, JSON, and TypeScript. I need a way to validate JSON and this is already built in under monaco.languages.json.DiagnosticsOptions. Could this be added?

Types for monaco not resoving

When I try to use:

import { monaco } from '@monaco-editor/react';

loader.then(instance => {});

the instance is not resolved as seen here:

2019-12-23_13-12-04

I'm wondering if its because you have monaco-editor as a peerDependency but you don't tell people to install it when they install the package?

Issues to set Model Markers

how to access setModelMarkers?
The only way I found to access it was to add the reference to the package monaco-editor and to call it the following way:

import * as monacoEditor from "monaco-editor/esm/vs/editor/editor.api";
/// blabla
monacoEditor.editor.setModelMarkers(myEditor.getModel(), "ScriptEditor", []);

the pb is that from the moment I make an actual use of monacoEditor.editor in the code, the editor doesn't behave correctly anymore: ctrl+arrow doesn't work, syntaxic coloration doesn't work, autocomplete doesn't work...

I believe that the import overrides some calls that are done in your version your are exposing, and ruins the behavior. I assume this because even if I make the following I still have the pb:

if(false){
    // this will never be called, but still, the compiler feels compelled to integrate 
    // the code in the final bundle, and that causes the issue as well
    monacoEditor.editor.setModelMarkers(myEditor.getModel(), "ScriptEditor", []);
}

here the working/failling example: https://codesandbox.io/s/funny-sara-kbws9?fontsize=14&hidenavigation=1&theme=dark

FYI, I would need to access some types that are necessary, like IStandaloneCodeEditor, or MarkerSeverity but it is as well impossible to access it. I think you should expose the full api of monaco, or at least a wrapper for most common features.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.