Skip to content

Commit

Permalink
create base structure
Browse files Browse the repository at this point in the history
  • Loading branch information
suren-atoyan committed Jun 16, 2019
0 parents commit 044f4be
Show file tree
Hide file tree
Showing 16 changed files with 372 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules/
yarn-error.log
16 changes: 16 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
sudo: false

language: node_js
node_js:
- 8

before_install:
- npm install codecov.io coveralls

after_success:
- cat ./coverage/lcov.info | ./node_modules/codecov.io/bin/codecov.io.js
- cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js

branches:
only:
- master
30 changes: 30 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"name": "monaco-react",
"version": "0.0.1",
"description": "Monaco Editor for React",
"main": "lib/index.js",
"module": "es/index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/SurenAt93/monaco-react.git"
},
"keywords": ["monaco","editor","react","create-react-app","CRA"],
"author": "suren_at",
"license": "MIT",
"bugs": {
"url": "https://github.com/SurenAt93/monaco-react/issues"
},
"homepage": "https://github.com/SurenAt93/monaco-react#readme",
"dependencies": {
"classnames": "^2.2.6",
"prop-types": "^15.7.2"
},
"devDependencies": {
"monaco-editor": "^0.17.0",
"react": "^16.8.6",
"react-dom": "^16.8.6"
}
}
100 changes: 100 additions & 0 deletions src/Editor/Editor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import React, { useState, useRef } from 'react';
import PropTypes from 'prop-types';

import Loading from '../Loading';

import { monaco } from '../utils';
import { useMount, useUpdate } from '../utils/hooks';

import config from '../config';

import classNames from 'classnames';

const wrapperStyles = {
display: 'flex',
position: 'relative',
};

const Editor =
({ value, language, theme, options, editorDidMount, line, loading }) =>
{
const [isLoading, setIsLoading] = useState(true);
const editorRef = useRef();
const monacoRef = useRef();
const containerRef = useRef();

useMount(_ => {
monaco
.init()
.then(monaco => (monacoRef.current = monaco) && createEditor());

return removeEditor;
});

useUpdate(_ => {
editorRef.current.setValue(value);
}, [value]);

useUpdate(_ => {
monacoRef.current.editor.setModelLanguage(editorRef.current.getModel(), language);
}, [language]);

useUpdate(_ => {
editorRef.current.setScrollPosition({ scrollTop: line });
}, [line]);

useUpdate(_ => {
monacoRef.current.editor.setTheme(theme);
}, [theme]);

function createEditor() {
editorRef.current = monacoRef.current.editor.create(containerRef.current, {
value,
language,
automaticLayout: true,
...options,
});

editorDidMount &&
editorDidMount(editorRef.current.getValue.bind(editorRef.current), editorRef.current);

monacoRef.current.editor.defineTheme('dark', config.theme['night-dark']);
monacoRef.current.editor.setTheme(theme);

setIsLoading(false);
}

function removeEditor() {
editorRef.current.dispose();
}

return (
<section className='full-size' style={wrapperStyles}>
{isLoading && <Loading content={loading} />}
<div
ref={containerRef}
className="full-size"
/>
</section>
);
};

Editor.propTypes = {
value: PropTypes.string,
language: PropTypes.string,
options: PropTypes.object,
editorDidMount: PropTypes.func,
theme: PropTypes.string,
line: PropTypes.number,
loading: PropTypes.oneOfType([PropTypes.element, PropTypes.string]),
};

Editor.defaultProps = {
language: 'javascript',
value: '',
options: {},
editorDidMount: _ => {},
loading: 'Loading...',
};

export default Editor;
5 changes: 5 additions & 0 deletions src/Editor/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { memo } from 'react';

import Editor from './Editor';

export default memo(Editor);
15 changes: 15 additions & 0 deletions src/Loading/Loading.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from 'react';

const loadingStyles = {
display: 'flex',
height: '100%',
width: '100%',
justifyContent: 'center',
alignItems: 'center',
};

function Loading({ content }) {
return <div style={loadingStyles}>{content}</div>;
}

export default Loading;
3 changes: 3 additions & 0 deletions src/Loading/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import Loading from './Loading';

export default Loading;
19 changes: 19 additions & 0 deletions src/config/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
const config = {
urls: {
monacoLoader: '/monaco-editor/vs/loader.js',
monacoBase: '/monaco-editor/vs',
},

theme: {
'night-dark': {
base: 'vs-dark',
inherit: true,
rules: [],
colors: {
'editor.background': '#202124',
},
},
},
}

export default config;
4 changes: 4 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import Editor from './Editor';

export default Editor;

5 changes: 5 additions & 0 deletions src/utils/hooks/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import useMount from './useMount';
import useUpdate from './useUpdate';
import useEditor from './useEditor';

export { useMount, useUpdate, useEditor };
19 changes: 19 additions & 0 deletions src/utils/hooks/useEditor/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// WARNING: this hack is just for being able to use
// monaco value getter in other components also.
// In general, it's not a normal usage of hooks,
// and it's totally workaround. Don't use this
// hook in any other places.

import { useState } from 'react';

let getEditorValue;

const useEditor = _ => {
const [getter, setGetter] = useState(getEditorValue);

!getEditorValue && (getEditorValue = getter);

return [getEditorValue, setGetter];
};

export default useEditor;
5 changes: 5 additions & 0 deletions src/utils/hooks/useMount/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { useEffect } from 'react';

const useMount = effect => useEffect(effect, []);

export default useMount;
14 changes: 14 additions & 0 deletions src/utils/hooks/useUpdate/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { useEffect, useRef } from 'react';

const useUpdate = (effect, deps) => {
const isInitialMount = useRef(true);

useEffect(
isInitialMount.current
? _ => { isInitialMount.current = false }
: effect,
deps
);
};

export default useUpdate;
3 changes: 3 additions & 0 deletions src/utils/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import monaco from './monaco';

export { monaco };
60 changes: 60 additions & 0 deletions src/utils/monaco.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
class Monaco {
init() {
if (window.monaco && window.monaco.editor) {
return new Promise((res, rej) => res(window.monaco));
}

document.addEventListener('monaco_init', this.handleMainScriptLoad);

const mainScript = this.createMainScript();

const loaderScript = this.createMonacoLoaderScript(mainScript);

this.injectScripts(loaderScript);

return new Promise((res, rej) => {
this.resolve = res;
this.reject = rej;
});
}

injectScripts(script) {
document.body.appendChild(script);
}

handleMainScriptLoad = () => {
document.removeEventListener('monaco_init', this.handleMainScriptLoad);
this.resolve(window.monaco);
}

createScript(src) {
const script = document.createElement('script');
return (src && (script.src = src), script);
}

createMonacoLoaderScript(mainScript) {
const loaderScript = this.createScript('/monaco-editor/vs/loader.js');
loaderScript.onload = _ => this.injectScripts(mainScript);

loaderScript.onerror = this.reject;

return loaderScript;
}

createMainScript() {
const mainScript = this.createScript();

mainScript.innerHTML = `
require.config({ paths: { 'vs': '/monaco-editor/vs' }});
require(['vs/editor/editor.main'], function() {
document.dispatchEvent(new Event('monaco_init'));
});
`;

mainScript.onerror = this.reject;

return mainScript;
}
}

export default new Monaco();
72 changes: 72 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1


classnames@^2.2.6:
version "2.2.6"
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce"
integrity sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==

"js-tokens@^3.0.0 || ^4.0.0":
version "4.0.0"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==

loose-envify@^1.1.0, loose-envify@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
dependencies:
js-tokens "^3.0.0 || ^4.0.0"

monaco-editor@^0.17.0:
version "0.17.0"
resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.17.0.tgz#d1d035c6f512ab88503cbee8213cab34b85ab954"
integrity sha512-8BQQHCFxy3DF0GYFOy5BmcCWlwm/XaTMPbPbN4gwItFGctZErSfX82uQSBpojJSlPNyudB5Q5qnukoorD3/UuA==

object-assign@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=

prop-types@^15.6.2, prop-types@^15.7.2:
version "15.7.2"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
dependencies:
loose-envify "^1.4.0"
object-assign "^4.1.1"
react-is "^16.8.1"

react-dom@^16.8.6:
version "16.8.6"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.8.6.tgz#71d6303f631e8b0097f56165ef608f051ff6e10f"
integrity sha512-1nL7PIq9LTL3fthPqwkvr2zY7phIPjYrT0jp4HjyEQrEROnw4dG41VVwi/wfoCneoleqrNX7iAD+pXebJZwrwA==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
prop-types "^15.6.2"
scheduler "^0.13.6"

react-is@^16.8.1:
version "16.8.6"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.6.tgz#5bbc1e2d29141c9fbdfed456343fe2bc430a6a16"
integrity sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==

react@^16.8.6:
version "16.8.6"
resolved "https://registry.yarnpkg.com/react/-/react-16.8.6.tgz#ad6c3a9614fd3a4e9ef51117f54d888da01f2bbe"
integrity sha512-pC0uMkhLaHm11ZSJULfOBqV4tIZkx87ZLvbbQYunNixAAvjnC+snJCg0XQXn9VIsttVsbZP/H/ewzgsd5fxKXw==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
prop-types "^15.6.2"
scheduler "^0.13.6"

scheduler@^0.13.6:
version "0.13.6"
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.13.6.tgz#466a4ec332467b31a91b9bf74e5347072e4cd889"
integrity sha512-IWnObHt413ucAYKsD9J1QShUKkbKLQQHdxRyw73sw4FN26iWr3DY/H34xGPe4nmL1DwXyWmSWmMrA9TfQbE/XQ==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"

0 comments on commit 044f4be

Please sign in to comment.