diff --git a/integration/__tests__/cluster-pages.tests.ts b/integration/__tests__/cluster-pages.tests.ts
index be032afab3..1fa5970bc1 100644
--- a/integration/__tests__/cluster-pages.tests.ts
+++ b/integration/__tests__/cluster-pages.tests.ts
@@ -433,7 +433,7 @@ utils.describeIf(minikubeReady(TEST_NAMESPACE))("Minikube based tests", () => {
await frame.waitForTimeout(100_000);
}
- const inputField = await frame.waitForSelector(".CreateResource div.react-monaco-editor-container");
+ const inputField = await frame.waitForSelector(".CreateResource .MonacoEditor");
await inputField.click();
await inputField.type("apiVersion: v1", { delay: 10 });
diff --git a/package.json b/package.json
index f93a37a352..6a37700fe7 100644
--- a/package.json
+++ b/package.json
@@ -232,7 +232,6 @@
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-material-ui-carousel": "^2.3.1",
- "react-monaco-editor": "^0.44.0",
"react-router": "^5.2.0",
"react-virtualized-auto-sizer": "^1.0.6",
"readable-stream": "^3.6.0",
diff --git a/src/renderer/components/+add-cluster/add-cluster.tsx b/src/renderer/components/+add-cluster/add-cluster.tsx
index ac6a2de5cd..e0a5a7c608 100644
--- a/src/renderer/components/+add-cluster/add-cluster.tsx
+++ b/src/renderer/components/+add-cluster/add-cluster.tsx
@@ -122,6 +122,7 @@ export class AddCluster extends React.Component {
{
this.customConfig = value;
diff --git a/src/renderer/components/monaco-editor/monaco-editor.scss b/src/renderer/components/monaco-editor/monaco-editor.scss
index 8fcf112750..caf1ef34db 100644
--- a/src/renderer/components/monaco-editor/monaco-editor.scss
+++ b/src/renderer/components/monaco-editor/monaco-editor.scss
@@ -20,7 +20,7 @@
*/
.MonacoEditor {
- width: inherit;
- height: inherit;
+ width: 100%;
+ height: 100%;
flex: 1;
}
diff --git a/src/renderer/components/monaco-editor/monaco-editor.tsx b/src/renderer/components/monaco-editor/monaco-editor.tsx
index 527ed92ab9..c44b664f23 100644
--- a/src/renderer/components/monaco-editor/monaco-editor.tsx
+++ b/src/renderer/components/monaco-editor/monaco-editor.tsx
@@ -21,27 +21,45 @@
import "./monaco-editor.scss";
import React from "react";
-import { computed, makeObservable, observable, reaction, toJS, when } from "mobx";
-import { disposeOnUnmount, observer } from "mobx-react";
-import * as monaco from "monaco-editor";
+import { action, computed, makeObservable, observable, reaction, toJS, when } from "mobx";
+import { observer } from "mobx-react";
+import { editor, Uri } from "monaco-editor";
import logger from "../../../common/logger";
-import ReactMonacoEditor, { EditorDidMount, MonacoEditorProps } from "react-monaco-editor";
import { ThemeStore } from "../../theme.store";
import { UserStore } from "../../../common/user-store";
-import { cssNames } from "../../utils";
+import { cssNames, disposer } from "../../utils";
-interface Props extends MonacoEditorProps {
- id?: string; // associate editor's model.uri with some ID (e.g. DockStore.TabId)
+export interface MonacoEditorProps {
+ id?: string; // associating editor's ID with created model.uri
+ value?: string; // initial text value for editor
className?: string;
autoFocus?: boolean;
readOnly?: boolean;
theme?: "vs" /* default, light theme */ | "vs-dark" | "hc-black" | string;
language?: "yaml" | "json"; // configure bundled list of languages in via MonacoWebpackPlugin({languages: []})
- onError?(error: string): void; // TODO: validation or some another occurred error
+ options?: editor.IStandaloneEditorConstructionOptions; // customize editor's initialization options
+ onChange?: onChangeCallback;
+ onError?: onErrorCallback;
+ onDidLayoutChange?(info: editor.EditorLayoutInfo): void;
+ onDidContentSizeChange?(evt: editor.IContentSizeChangedEvent): void;
}
-export const defaultEditorProps: Partial = {
+export interface onChangeCallback {
+ (value: string, data: {
+ model: editor.ITextModel, // current model
+ event: editor.IModelContentChangedEvent;
+ }): void;
+}
+
+export interface onErrorCallback {
+ (error: string): void; // TODO: provide validation or some another occurred error
+}
+
+export const defaultEditorProps: Partial = {
+ value: "",
+ options: {},
language: "yaml",
+ autoFocus: false,
get theme(): string {
// theme for monaco-editor defined in `src/renderer/themes/lens-*.json`
return ThemeStore.getInstance().activeTheme.monacoTheme;
@@ -49,28 +67,32 @@ export const defaultEditorProps: Partial = {
};
@observer
-export class MonacoEditor extends React.Component {
+export class MonacoEditor extends React.Component {
static defaultProps = defaultEditorProps as object;
- static models = new WeakMap();
- static viewStates = new WeakMap();
+ static models = new WeakMap();
+ static viewStates = new WeakMap();
- @observable.ref editor: monaco.editor.IStandaloneCodeEditor;
public staticId = `editor-id#${Math.round(1e7 * Math.random())}`;
+ public disposeOnUnmount = disposer();
- constructor(props: Props) {
+ @observable.ref containerElem: HTMLElement;
+ @observable.ref editor: editor.IStandaloneCodeEditor;
+ @observable dimensions: { width?: number, height?: number } = {};
+ @observable unmounting = false;
+
+ constructor(props: MonacoEditorProps) {
super(props);
makeObservable(this);
+
MonacoEditor.models.set(this, []);
-
- disposeOnUnmount(this, [
- reaction(() => this.model, this.onModelChange),
- ]);
-
- this.whenReady.then(() => this.bindResizeObserver());
}
- get whenReady() {
- return when(() => Boolean(this.editor));
+ get whenEditorReady() {
+ return when(() => Boolean(this.containerElem && this.editor));
+ }
+
+ get whenUnmounting() {
+ return when(() => this.unmounting);
}
/**
@@ -82,8 +104,8 @@ export class MonacoEditor extends React.Component {
for (const entry of entries) {
const { width, height } = entry.contentRect;
- this.editor.layout({ width, height });
logger.info(`[MONACO]: refreshing dimensions to width=${width} and height=${height}`, entry);
+ this.setDimensions(width, height);
}
});
@@ -94,16 +116,16 @@ export class MonacoEditor extends React.Component {
return () => resizeObserver.unobserve(containerElem);
}
- onModelChange = (model: monaco.editor.ITextModel, oldModel?: monaco.editor.ITextModel) => {
+ onModelChange = (model: editor.ITextModel, oldModel?: editor.ITextModel) => {
logger.info("[MONACO]: model change", { model, oldModel });
+
this.saveViewState(oldModel); // save previously used view-model state
this.editor.setModel(model);
- this.editor.restoreViewState(this.getViewState(model));
+ this.editor.restoreViewState(this.getViewState(model)); // restore cursor position, selection, etc.
this.editor.layout();
- this.editor.focus();
};
- @computed get model(): monaco.editor.ITextModel {
+ @computed get model(): editor.ITextModel {
const { language, value, id = this.staticId } = this.props;
const model = this.getModelById(id);
@@ -112,34 +134,94 @@ export class MonacoEditor extends React.Component {
// creating new temporary model
const uri = this.createUri(id);
- const newModel = monaco.editor.createModel(value, language, uri);
+ const newModel = editor.createModel(value, language, uri);
- MonacoEditor.models.get(this).push(newModel);
logger.info(`[MONACO]: creating new model ${uri}`, newModel);
+ MonacoEditor.models.get(this).push(newModel);
return newModel;
}
- editorDidMount: EditorDidMount = (editor, monaco) => {
- this.props.editorDidMount?.(editor, monaco);
- this.editor = editor;
+ @computed get globalEditorOptions() {
+ return toJS(UserStore.getInstance().editorConfiguration);
+ }
+
+ componentDidMount() {
+ this.createEditor();
+ logger.info(`[MONACO]: editor did mounted`);
if (this.props.autoFocus) {
this.editor.focus();
}
- };
+ }
componentWillUnmount() {
logger.info(`[MONACO]: unmounting editor..`);
+ this.unmounting = true;
+ this.disposeOnUnmount();
MonacoEditor.models.get(this).forEach(model => model.dispose());
MonacoEditor.models.delete(this);
+ this.editor?.dispose();
}
- createUri(id: string): monaco.Uri {
- return monaco.Uri.file(`/monaco-editor/${id}`);
+ private createEditor = (): void => {
+ if (!this.containerElem || this.editor || this.unmounting) {
+ return;
+ }
+ const { language, theme, readOnly, value: defaultValue, options } = this.props;
+
+ this.editor = editor.create(this.containerElem, {
+ model: this.model,
+ value: defaultValue,
+ language,
+ theme,
+ readOnly,
+ ...this.globalEditorOptions,
+ ...options,
+ });
+ logger.info(`[MONACO]: editor created for language=${language}, theme=${theme}`);
+
+ const onDidLayoutChangeDisposer = this.editor.onDidLayoutChange(layoutInfo => {
+ this.props.onDidLayoutChange?.(layoutInfo);
+ // logger.info("[MONACO]: onDidLayoutChange()", layoutInfo);
+ });
+
+ const onValueChangeDisposer = this.editor.onDidChangeModelContent(event => {
+ const value = this.editor.getValue();
+ // logger.info("[MONACO]: value changed", { value, event });
+
+ this.props.onChange?.(value, {
+ model: this.model,
+ event,
+ });
+ });
+
+ const onContentSizeChangeDisposer = this.editor.onDidContentSizeChange((params) => {
+ this.props.onDidContentSizeChange?.(params);
+ // logger.info("[MONACO]: onDidContentSizeChange():", params)
+ });
+
+ this.disposeOnUnmount.push(
+ reaction(() => this.model, this.onModelChange),
+ () => onDidLayoutChangeDisposer.dispose(),
+ () => onValueChangeDisposer.dispose(),
+ () => onContentSizeChangeDisposer.dispose(),
+ this.bindResizeObserver(),
+ );
+ };
+
+ @action
+ setDimensions(width: number, height: number) {
+ this.dimensions.width = width;
+ this.dimensions.height = height;
+ this.editor?.layout({ width, height });
}
- getViewState(model = this.model): monaco.editor.ICodeEditorViewState {
+ createUri(id: string): Uri {
+ return Uri.file(`/monaco-editor/${id}`);
+ }
+
+ getViewState(model = this.model): editor.ICodeEditorViewState {
return MonacoEditor.viewStates.get(model) ?? null;
}
@@ -148,7 +230,7 @@ export class MonacoEditor extends React.Component {
MonacoEditor.viewStates.set(model, this.editor.saveViewState());
}
- getModelById(id: string): monaco.editor.ITextModel | null {
+ getModelById(id: string): editor.ITextModel | null {
const uri = this.createUri(id);
for (const model of MonacoEditor.models.get(this)) {
@@ -166,23 +248,16 @@ export class MonacoEditor extends React.Component {
return this.editor.getValue(opts);
}
+ bindRef = (elem: HTMLElement) => this.containerElem = elem;
+
render() {
- const { autoFocus, readOnly, className, options = {}, ...reactMonacoEditorProps } = this.props;
- const globalOptions = toJS(UserStore.getInstance().editorConfiguration);
+ const { className } = this.props;
return (
-
-
-
+
);
}
}
diff --git a/webpack.renderer.ts b/webpack.renderer.ts
index 6e6b199d8a..345fc95b80 100755
--- a/webpack.renderer.ts
+++ b/webpack.renderer.ts
@@ -150,8 +150,10 @@ export function webpackLensRenderer({ showVars = true } = {}): webpack.Configura
new ProgressBarPlugin(),
new ForkTsCheckerPlugin(),
+ // see also: https://github.com/Microsoft/monaco-editor-webpack-plugin#options
new MonacoWebpackPlugin({
- // available options are documented at https://github.com/Microsoft/monaco-editor-webpack-plugin#options
+ // publicPath: "/",
+ // filename: "[name].worker.js",
languages: ["json", "yaml"],
globalAPI: isDevelopment,
}),
diff --git a/yarn.lock b/yarn.lock
index 3fc5e008f1..7a7f4c6c82 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4683,7 +4683,7 @@ debug@^3.0.0, debug@^3.1.0, debug@^3.1.1, debug@^3.2.6:
dependencies:
ms "^2.1.1"
-debuglog@*, debuglog@^1.0.1:
+debuglog@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492"
integrity sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI=
@@ -7392,7 +7392,7 @@ import-local@^3.0.2:
pkg-dir "^4.2.0"
resolve-cwd "^3.0.0"
-imurmurhash@*, imurmurhash@^0.1.4:
+imurmurhash@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
integrity sha1-khi5srkoojixPcT7a21XbyMUU+o=
@@ -9160,11 +9160,6 @@ lodash-es@^4.17.20:
resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee"
integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==
-lodash._baseindexof@*:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/lodash._baseindexof/-/lodash._baseindexof-3.1.0.tgz#fe52b53a1c6761e42618d654e4a25789ed61822c"
- integrity sha1-/lK1OhxnYeQmGNZU5KJXie1hgiw=
-
lodash._baseuniq@~4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/lodash._baseuniq/-/lodash._baseuniq-4.6.0.tgz#0ebb44e456814af7905c6212fa2c9b2d51b841e8"
@@ -9173,33 +9168,11 @@ lodash._baseuniq@~4.6.0:
lodash._createset "~4.0.0"
lodash._root "~3.0.0"
-lodash._bindcallback@*:
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz#e531c27644cf8b57a99e17ed95b35c748789392e"
- integrity sha1-5THCdkTPi1epnhftlbNcdIeJOS4=
-
-lodash._cacheindexof@*:
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/lodash._cacheindexof/-/lodash._cacheindexof-3.0.2.tgz#3dc69ac82498d2ee5e3ce56091bafd2adc7bde92"
- integrity sha1-PcaayCSY0u5ePOVgkbr9Ktx73pI=
-
-lodash._createcache@*:
- version "3.1.2"
- resolved "https://registry.yarnpkg.com/lodash._createcache/-/lodash._createcache-3.1.2.tgz#56d6a064017625e79ebca6b8018e17440bdcf093"
- integrity sha1-VtagZAF2JeeevKa4AY4XRAvc8JM=
- dependencies:
- lodash._getnative "^3.0.0"
-
lodash._createset@~4.0.0:
version "4.0.3"
resolved "https://registry.yarnpkg.com/lodash._createset/-/lodash._createset-4.0.3.tgz#0f4659fbb09d75194fa9e2b88a6644d363c9fe26"
integrity sha1-D0ZZ+7CddRlPqeK4imZE02PJ/iY=
-lodash._getnative@*, lodash._getnative@^3.0.0:
- version "3.9.1"
- resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5"
- integrity sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=
-
lodash._root@~3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/lodash._root/-/lodash._root-3.0.1.tgz#fba1c4524c19ee9a5f8136b4609f017cf4ded692"
@@ -9225,11 +9198,6 @@ lodash.isequal@^4.5.0:
resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA=
-lodash.restparam@*:
- version "3.6.1"
- resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805"
- integrity sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU=
-
lodash.toarray@^4.4.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/lodash.toarray/-/lodash.toarray-4.4.0.tgz#24c4bfcd6b2fba38bfd0594db1179d8e9b656561"
@@ -9775,11 +9743,6 @@ monaco-editor-webpack-plugin@^4.2.0:
dependencies:
loader-utils "^2.0.0"
-monaco-editor@^0.26.1:
- version "0.26.1"
- resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.26.1.tgz#62bb5f658bc95379f8abb64b147632bd1c019d73"
- integrity sha512-mm45nUrBDk0DgZKgbD7+bhDOtcAFNGPJJRAdS6Su1kTGl6XEgC7U3xOmDUW/0RrLf+jlvCGaqLvD4p2VjwuwwQ==
-
monaco-editor@^0.28.1:
version "0.28.1"
resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.28.1.tgz#732788ff2172d59e6d436b206da8cac715413940"
@@ -11747,14 +11710,6 @@ react-material-ui-carousel@^2.3.1:
auto-bind "^2.1.1"
react-swipeable "^6.1.0"
-react-monaco-editor@^0.44.0:
- version "0.44.0"
- resolved "https://registry.yarnpkg.com/react-monaco-editor/-/react-monaco-editor-0.44.0.tgz#9f966fd00b6c30e8be8873a3fbc86f14a0da2ba4"
- integrity sha512-GPheXTIpBXpwv857H7/jA8HX5yae4TJ7vFwDJ5iTvy05LxIQTsD3oofXznXGi66lVA93ST/G7wRptEf4CJ9dOg==
- dependencies:
- monaco-editor "^0.26.1"
- prop-types "^15.7.2"
-
react-redux@^7.2.0:
version "7.2.3"
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.3.tgz#4c084618600bb199012687da9e42123cca3f0be9"