Last active
September 17, 2020 12:21
-
-
Save abersnaze/efac6927e17187550d4f5d795334ccae to your computer and use it in GitHub Desktop.
React component, in typescript, wrapping Monaco editor to automatically grow & shrink with content to avoid scroll.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React from 'react'; | |
import { storiesOf } from '@storybook/react'; | |
import { action } from '@storybook/addon-actions'; | |
import InlineMonacoEditor from './InlineMonacoEditor'; | |
export const LINES = [ | |
'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', | |
'Aenean aliquet, nulla eget auctor porttitor, lacus urna', | |
'posuere purus, at suscipit orci sapien quis est. Curabitur', | |
'vel erat pulvinar, congue ligula non, convallis urna.', | |
'Quisque finibus mauris a tempor facilisis. Nam molestie', | |
'nisl a eros gravida, et eleifend risus interdum. Aliquam', | |
'faucibus et diam vel lacinia. Aenean bibendum enim nisi,', | |
'dignissim commodo tortor tincidunt sit amet. Mauris mollis', | |
'enim at leo volutpat, ac iaculis nunc scelerisque. Aliquam', | |
'nec accumsan magna. Proin nec justo sed nulla vehicula', | |
'dictum eu sed nisi. Maecenas commodo odio felis, consequat', | |
'consequat tellus vestibulum ac. Etiam tempor eu purus at', | |
'pharetra. Nulla tellus lectus, venenatis id ultricies eget,', | |
'euismod vel odio. Phasellus in orci in mi placerat interdum', | |
'vitae vel tellus. Morbi a lectus semper diam condimentum', | |
'efficitur quis eget leo. Sed hendrerit ante in metus', | |
'pellentesque, eu iaculis est aliquam. Suspendisse arcu', | |
'sapien, lobortis egestas euismod at, tincidunt non nibh.' | |
]; | |
export const actions = { | |
editorDidMount: action('editorDidMount'), | |
}; | |
storiesOf('InlineEditor', module) | |
.add('zero', () => <div> | |
<hr /> | |
<InlineMonacoEditor {...actions} /> | |
<hr /> | |
</div>) | |
.add('one', () => <div> | |
<hr /> | |
<InlineMonacoEditor defaultValue={LINES[0]} {...actions} /> | |
<hr /> | |
</div>) | |
.add('many', () => <div> | |
<hr /> | |
<InlineMonacoEditor defaultValue={LINES.join('\n')} {...actions} /> | |
<hr /> | |
</div>) | |
.add('long', () => <div> | |
<hr /> | |
<InlineMonacoEditor defaultValue={LINES.join(' ')} {...actions} /> | |
<hr /> | |
</div>); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import * as monacoEditor from 'monaco-editor/esm/vs/editor/editor.api'; | |
import React from 'react'; | |
import MonacoEditor, { EditorDidMount, MonacoEditorProps } from 'react-monaco-editor'; | |
const LINE_HEIGHT = 18; | |
const DEFAULT_STATE = { | |
editor: undefined as unknown as monacoEditor.editor.ICodeEditor, | |
prevLineCount: -1 | |
} | |
export default class InlineMonacoEditor extends React.Component<MonacoEditorProps, typeof DEFAULT_STATE> { | |
constructor(props: MonacoEditorProps) { | |
super(props); | |
this.state = DEFAULT_STATE; | |
} | |
componentWillUnmount() { | |
if (window) { | |
window.removeEventListener('resize', this.setEditorHeight); | |
} | |
} | |
render() { | |
const { options = {}, editorDidMount } = this.props; | |
// override a word wrapping, disable and hide the scroll bars | |
const optionsOverride = { | |
...options, | |
wordWrap: 'on', | |
scrollBeyondLastLine: false, | |
scrollbar: { | |
vertical: 'hidden', | |
horizontal: 'hidden', | |
}, | |
} as monacoEditor.editor.IEditorConstructionOptions; | |
return ( | |
<MonacoEditor | |
{...this.props} | |
editorDidMount={this.editorDidMount(editorDidMount)} | |
options={optionsOverride} /> | |
); | |
} | |
private editorDidMount(prevEditorDidMount: EditorDidMount | undefined): EditorDidMount { | |
return (editor, monaco) => { | |
// chain an pre-existing editorDidMount handler | |
if (prevEditorDidMount) { | |
prevEditorDidMount(editor, monaco); | |
} | |
// put the edit in the state for the handler. | |
this.setState({ editor }); | |
// do the initial set of the height (wait a bit) | |
setTimeout(this.setEditorHeight, 0); | |
// adjust height when the window resizes | |
if (window) { | |
window.addEventListener("resize", this.setEditorHeight); | |
} | |
// on each edit recompute height (wait a bit) | |
editor.onDidChangeModelDecorations(() => { | |
setTimeout(this.setEditorHeight, 0) | |
}); | |
}; | |
} | |
setEditorHeight = (() => { | |
const { editor, prevLineCount } = this.state; | |
if (!editor) { return; } | |
const editorDomNode = editor.getDomNode(); | |
if (!editorDomNode) { return; } | |
const container = editorDomNode.getElementsByClassName('view-lines')[0] as HTMLElement; | |
const containerHeight = container.offsetHeight; | |
const lineHeight = container.firstChild | |
? (container.firstChild as HTMLElement).offsetHeight | |
: LINE_HEIGHT; | |
if (!containerHeight) { | |
// dom hasn't finished settling down. wait a bit more. | |
setTimeout(this.setEditorHeight, 0); | |
} else { | |
const currLineCount = container.childElementCount; | |
const nextHeight = (prevLineCount > currLineCount) | |
// if line count is shrinking monaco tends to leave the extra | |
// space at the end, compute the height from the line count | |
? currLineCount * lineHeight | |
// otherwise use the height of the container div as the height | |
// of the editor node | |
: containerHeight; | |
// set the height and redo layout | |
editorDomNode.style.height = nextHeight + 'px'; | |
editor.layout(); | |
// double check that layout didn't change things too much | |
if (container.childElementCount !== currLineCount) { | |
this.setEditorHeight(); | |
} else { | |
this.setState({ prevLineCount: currLineCount }) | |
} | |
} | |
}).bind(this);// bind now so addEventListener/removeEventListener works. | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment