Skip to content

Commit ea1ae71

Browse files
committed
Merge branch 'joaojmendes-monacoEditor' into dev
2 parents 9370d9c + 0205b5b commit ea1ae71

File tree

14 files changed

+347
-108
lines changed

14 files changed

+347
-108
lines changed
2.19 MB
Loading
2.19 MB
Loading
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Monaco Editor control
2+
3+
This control is a implementatiopn of Monaco Editor.
4+
5+
Here is an example of the control:
6+
7+
![monacoeditor](../assets/MonacoEditor1.png)
8+
9+
`MonacoEditor` dark theme:
10+
11+
![monacoeditor](../assets/MonacoEditor2.png)
12+
13+
14+
## How to use this control in your solutions
15+
16+
- Check that you installed the `@pnp/spfx-controls-react` dependency. Check out the [getting started](../../#getting-started) page for more information about installing the dependency.
17+
- Import the control into your component:
18+
19+
```TypeScript
20+
import { MonacoEditor } from "@pnp/spfx-controls-react/lib/MonacoEditor";
21+
```
22+
23+
- Use the `MonacoEditor` control in your code as follows:
24+
25+
```TypeScript
26+
<MonacoEditor value={defaultValue}
27+
showMiniMap={true}
28+
onValueChange={onValueChange}
29+
language={"javascript"}/>
30+
```
31+
32+
- The `onValueChange` change event returns the upadated code and array with messages of errors on validation and can be implemented as follows:
33+
**this validation is done only to JSON language
34+
35+
```TypeScript
36+
const onValueChange = React.useCallback((newValue: string, validationErrors: string[]): void => {console.log(newValue);} , []);
37+
```
38+
39+
## Implementation
40+
41+
The `MonacoEditor` control can be configured with the following properties:
42+
43+
| Property | Type | Required | Description |
44+
| ---- | ---- | ---- | ---- |
45+
| value | string | yes | default content for editor |
46+
| theme | string | no | theme used by editor , two themes are supported 'vs' and 'vs-dark', default 'vs' |
47+
| readOnly | boolean | no | indecate if editor is in read only mode |
48+
| showLineNumbers | boolean | no | editor show linenumber or not, default : yes|
49+
| onValueChange |(newValue:string, validationErrors:string[]) => void | no | function to get code changes, return an array with validation error in case of language is 'JSON' |
50+
| language |string | yes | editor code language, please see https://microsoft.github.io/monaco-editor/index.html for supported languages|
51+
| jsonDiagnosticsOptions |monaco.languages.json.DiagnosticsOptions | no | define options to JSON validation, please see https://microsoft.github.io/monaco-editor/index.html for more details |
52+
| jscriptDiagnosticsOptions | monaco.languages.typescript.DiagnosticsOptions | no | define options to javascript or typescript validation, please see https://microsoft.github.io/monaco-editor/index.html for more details |
53+
54+

package-lock.json

Lines changed: 38 additions & 20 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
"@microsoft/sp-office-ui-fabric-core": "1.14.0",
3535
"@microsoft/sp-property-pane": "1.14.0",
3636
"@microsoft/sp-webpart-base": "1.14.0",
37+
"@monaco-editor/loader": "^1.2.0",
3738
"@pnp/sp": "2.5.0",
3839
"@pnp/telemetry-js": "2.0.0",
3940
"@popperjs/core": "2.5.4",
@@ -48,6 +49,7 @@
4849
"he": "^1.2.0",
4950
"lodash": "4.17.21",
5051
"markdown-it": "^12.3.2",
52+
"monaco-editor": "^0.32.1",
5153
"office-ui-fabric-react": "7.174.1",
5254
"react": "16.13.1",
5355
"react-accessible-accordion": "^3.3.3",
@@ -96,6 +98,7 @@
9698
"spfx-fast-serve-helpers": "~1.13.0",
9799
"ts-jest": "^25.5.1",
98100
"tslib": "2.0.0",
101+
"tslint-microsoft-contrib": "6.2.0",
99102
"webpack-bundle-analyzer": "^4.1.0"
100103
},
101104
"repository": {

src/controls/listPicker/IListPicker.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
import { LibsOrderBy } from "../../services/ISPService";
2-
import { BaseComponentContext } from '@microsoft/sp-component-base';
31
import { IDropdownOption } from "office-ui-fabric-react/lib/Dropdown";
42

3+
import { BaseComponentContext } from "@microsoft/sp-component-base";
4+
5+
import { LibsOrderBy } from "../../services/ISPService";
6+
57
export interface IListPickerProps {
68
/**
79
* The web part context

src/controls/monacoEditor/Error.tsx

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
2+
import { Stack } from "office-ui-fabric-react/lib/components/Stack";
3+
import { MessageBarType , MessageBar} from "office-ui-fabric-react/lib/MessageBar";
4+
import * as React from "react";
5+
6+
export interface IErrorProps {
7+
error: Error;
8+
show: boolean;
9+
}
10+
11+
export const Error: React.FunctionComponent<IErrorProps> = (props: React.PropsWithChildren<IErrorProps>) => {
12+
const { error, show } = props;
13+
return (
14+
<>
15+
(show && error) ?
16+
<Stack horizontal horizontalAlign="start">
17+
<MessageBar isMultiline messageBarType={MessageBarType.error}>
18+
{error.message}
19+
</MessageBar>
20+
</Stack>
21+
: null;
22+
</>
23+
);
24+
};
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import * as monaco from "monaco-editor";
2+
3+
export enum Elanguages {
4+
typescript = 'typescript',
5+
javascript = 'javascript',
6+
css = 'css',
7+
html = 'html',
8+
json = 'json',
9+
xml = 'xml',
10+
markdown = 'markdown',
11+
less = 'less',
12+
scss = 'scss',
13+
handlebars = 'handlebars',
14+
15+
16+
}
17+
export interface IMonacoEditorProps {
18+
value: string;
19+
theme?: string;
20+
readOnly?: boolean;
21+
showLineNumbers?: boolean;
22+
showMiniMap?: boolean;
23+
onValueChange?: (newValue:string, validationErrors:string[]) => void;
24+
language: string | Elanguages;
25+
jsonDiagnosticsOptions?: monaco.languages.json.DiagnosticsOptions;
26+
jscriptDiagnosticsOptions?: monaco.languages.typescript.DiagnosticsOptions;
27+
28+
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import { isEmpty } from "lodash";
2+
import * as React from "react";
3+
import { Elanguages } from ".";
4+
import { IMonacoEditorProps } from "./IMonacoEditorProps";
5+
import { useMonacoEditorStyles } from "./useMonacoEditorStyles";
6+
import { EStatus, useMonaco } from "./useMonaco";
7+
import { Spinner, SpinnerSize } from "office-ui-fabric-react/lib/Spinner";
8+
import { Stack } from "office-ui-fabric-react/lib/Stack";
9+
import { Error } from "./Error";
10+
11+
export const MonacoEditor: React.FunctionComponent<IMonacoEditorProps> = (
12+
props: React.PropsWithChildren<IMonacoEditorProps>
13+
) => {
14+
const {
15+
value,
16+
onValueChange,
17+
theme,
18+
readOnly,
19+
showLineNumbers,
20+
showMiniMap,
21+
language,
22+
jsonDiagnosticsOptions,
23+
jscriptDiagnosticsOptions,
24+
} = props || ({} as IMonacoEditorProps);
25+
26+
const containerRef = React.useRef<HTMLDivElement>(null);
27+
const editorRef = React.useRef<any>(null);
28+
const { controlClasses } = useMonacoEditorStyles();
29+
const { monaco, status, error } = useMonaco();
30+
31+
const onDidChangeModelContent = React.useCallback(
32+
(e: any): void => {
33+
if (editorRef.current) {
34+
let currentValue: string = editorRef.current.getValue();
35+
if (currentValue !== value) {
36+
let validationErrors: string[] = [];
37+
try {
38+
if (language === Elanguages.json) {
39+
let jsonParsed: any = JSON.parse(currentValue);
40+
}
41+
} catch (e) {
42+
validationErrors.push(e.message);
43+
}
44+
console.log(currentValue);
45+
onValueChange(currentValue, validationErrors);
46+
}
47+
}
48+
},
49+
[onValueChange]
50+
);
51+
52+
React.useEffect(() => {
53+
if (status != EStatus.LOADED) return;
54+
55+
if (!isEmpty(jsonDiagnosticsOptions) && language === Elanguages.json) {
56+
monaco.languages.json.jsonDefaults.setDiagnosticsOptions(jsonDiagnosticsOptions);
57+
}
58+
if (!isEmpty(jscriptDiagnosticsOptions) && language === Elanguages.javascript) {
59+
monaco.languages.typescript.javascriptDefaults.setDiagnosticsOptions(jscriptDiagnosticsOptions);
60+
}
61+
62+
monaco.editor.onDidCreateModel((m: any) => {
63+
m.updateOptions({
64+
tabSize: 2,
65+
});
66+
});
67+
68+
//Create the MonacoEditor
69+
editorRef.current = monaco.editor.create(containerRef.current, {
70+
value: value,
71+
scrollBeyondLastLine: false,
72+
theme: theme,
73+
language: language,
74+
folding: true,
75+
readOnly: readOnly,
76+
lineNumbersMinChars: 4,
77+
lineNumbers: showLineNumbers ? "on" : "off",
78+
minimap: {
79+
enabled: showMiniMap,
80+
},
81+
});
82+
83+
editorRef.current.onDidChangeModelContent(onDidChangeModelContent);
84+
return () => {
85+
editorRef?.current?.dispose();
86+
};
87+
}, [jsonDiagnosticsOptions, jscriptDiagnosticsOptions, monaco]);
88+
89+
if (status === EStatus.LOADING) {
90+
return (
91+
<Stack horizontal horizontalAlign="center" tokens={{ padding: 25 }}>
92+
<Spinner size={SpinnerSize.medium} />
93+
</Stack>
94+
);
95+
}
96+
if (status === EStatus.ERROR) {
97+
return <Error error={error} show={true} />;
98+
}
99+
return (
100+
<>
101+
<div ref={containerRef} className={controlClasses.containerStyles} />
102+
</>
103+
);
104+
};

src/controls/monacoEditor/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './IMonacoEditorProps';
2+
export * from './MonacoEditor';

0 commit comments

Comments
 (0)