Monaco编辑器动态调整大小

43

我一直在搜索一个关于在使用Monaco Editor的输入框时是否可能模仿html标签textarea的调整大小的讨论,但是我在互联网上找不到一个能回答我的问题的讨论。

我正在React应用程序中使用monaco-editor npm包。您有任何想法是否容易实现吗?

非常感谢!

解决方案
通过纯css选择目标html元素,并添加以下属性:

div {
  resize: vertical;
  overflow: auto;
}

2
我建议您在答案部分回答您的问题,而不仅仅是编辑答案并接受它。这样会更好。 - Satnam Sandhu
1
这让我头疼不已。使用monaco-react只需五分钟,并且默认情况下它是响应式的。 - Parm
6个回答

76

TL;DR: 在编辑器的配置中添加automaticLayout: true

NL;PR:

Monaco内置了自动调整大小以适应父容器的功能:

    createEditorWithAutoResize(){
      this.editor = monaco.editor.create(
            this.editorDiv.current, {
            value: "var x = 0;",
            language: 'javascript',
            automaticLayout: true // <<== the important part
       }
      );
    }
    componentDidMount(){this.createEditorWithAutoResize();}
    constructor(props){super(props); this.editorDiv = React.createRef();}
    render(){return <div ref={this.editorDiv} className="editor" ></div>}

这是编辑器的 CSS(它避免了第一次呈现时高度只有大约10像素的情况):

    .editor{
     height: 100%;
    } 

首次测试:v0.10.1,最后测试:v0.32.1

注意:< v0.20.0:该机制不会监听其容器大小的更改,而是进行轮询。 [API]

@nrayburn-tech(Monaco Editor的贡献者):版本0.20在所有浏览器中使用MutationObserver。版本0.21及更高版本在支持的浏览器上使用ResizeObserver,否则将使用轮询作为后备方案。 


5
如果你需要高性能,不建议使用此方法。https://github.com/Microsoft/monaco-editor/issues/28 - atilkan
你读了那个注释吗?轮询 vs 监听?官方文档的参考资料? - David I. Samudio
奇怪。当我将automaticLayout设置为true并将编辑器的height设置为100%时,编辑器不会增长/调整大小。正如您所指出的,如果我不设置height,则第一次呈现编辑器为~`10px`,但仍然不会调整大小。 - Trevor
你使用的是哪个版本?我记得在0.2x版本左右有一个问题。当在此处添加选项时,它可以正确工作:https://microsoft.github.io/monaco-editor/playground.html#creating-the-editor-hello-world - David I. Samudio

17

对于任何在基本的 Web 应用程序(html、css、javascript)中遇到此问题的人,我已经找到了解决我所遇到的调整大小问题的方法。

我在可调整大小的 flex 容器内使用了 Monaco 编辑器。它只会增加宽度,不会缩小宽度,并且垂直调整大小似乎无法正常工作。

如果您使用 Monaco 配置 "automaticLayout: true" 和以下 CSS,则它似乎可以按预期调整大小:

.monaco-editor { position: absolute !important; }

我尝试了最大宽度 99% 的技巧,但当将宽度增加到页面边缘附近时,会导致延迟效果和卡顿。


1
谢谢,这对我很有帮助,而且非常简单。(我正在使用Javascript) - user638750
1
工作得像魔法一样顺利! - Bryan
1
我之前也遇到了同样的问题,这个方法似乎解决了它! - undefined
1
我至少花了一个小时玩弄自己的调整大小,使用window.onresizeResizeObserver,因为我担心automaticLayout似乎使用了一个间隔或其他什么...如果你现在也在这个阶段...不要像我一样做,只需这样做就可以了哈哈。完美无缺地运行。 - undefined

17

如果您有编辑器的引用,您只需在某些调整大小事件上调用editor.layout()。例如,在窗口调整大小时:

window.onresize = function (){
    editor.layout();
};

7
为了后代,我采用的解决方案是设置 automaticLayout: false,以便我可以在调整窗口大小的事件监听器中执行所有布局。
const placeholder = document.getElementById('placeholder')

const editor = monaco.editor.create(placeholder, {
  value: '// hello world',
  language: 'javascript',
  automaticLayout: false // or remove, it defaults to false
})

// we need the parent of the editor
const parent = placeholder.parentElement

window.addEventListener('resize', () => {
  // make editor as small as possible
  editor.layout({ width: 0, height: 0 })

  // wait for next frame to ensure last layout finished
  window.requestAnimationFrame(() => {
    // get the parent dimensions and re-layout the editor
    const rect = parent.getBoundingClientRect()
    editor.layout({ width: rect.width, height: rect.height })
  })
})

首先将编辑器的布局调整为0,这样我们就可以安全地查询父元素的尺寸而不会受到子元素(编辑器)对其大小的影响。然后我们可以将编辑器与新的父元素尺寸匹配。由于这个过程在单个帧中完成,因此不应出现闪烁或延迟。


这对我起了作用! - ShortDevelopment
在计算父元素大小之前将其缩小是有效的。谢谢! - Santiago Corredoira
我在使用 automaticLayout: false 时,小地图出现了一些闪烁,但是使用这种方法却很流畅...奇怪。 - fluffels

3

这是一个旧问题,但仍然存在这个问题,我使用react-resize-detector解决了它。

基于ResizeObserver,它完美地满足了需求(检查浏览器兼容性

组件示例:

import React, { Component } from 'react';
import ReactResizeDetector from 'react-resize-detector';
import * as monaco from 'monaco-editor';

class Editor extends Component {
    constructor(props) {
        super(props)

        this.state = {
            width: 0,
            height: 0,
        }
        this.editor_div = React.createRef()

        this.handle_rezise = this.handle_rezise.bind(this);
    }

    componentDidMount() {
        const editor_model = monaco.editor.createModel('', 'sql');
        this.monaco_editor = monaco.editor.create(this.editor_div.current, this.props.editorOptions);
        this.monaco_editor.setModel(editor_model);
    }

    componentWillUnmount() {
        this.monaco_editor && this.monaco_editor.dispose();
    }

    handle_rezise(width, height) {
        this.monaco_editor.layout({ height, width });
    }

    render() {
        return(
            <div 
                className="editor-container"
                style={{ height: '100%' }}>
                <ReactResizeDetector
                    handleWidth
                    handleHeight
                    onResize={ this.handle_rezise }
                    refreshMode="debounce"
                    refreshRate={100} />
                <div 
                    className="editor"
                    ref={ this.editor_div }
                    style={{ height: '100%' }} />
            </div>
        )
    }
}

export default Editor;

希望这有所帮助。


2
在我的情况下,我使用了完全相同的CSS,但是尽管automaticLayout:true有效,但我发现它过度杀伤(似乎在100毫秒间隔内汇集DOM,并且我在文件中打开了几个编辑器。因此,我最终手动实现了它:

以防万一,我的需求不同:我希望用户以标准方式和廉价(在代码和性能方面)调整容器大小。这就是我所做的:

css容器:resize:vertical; overflow:auto

以及这个js:

function installResizeWatcher(el, fn, interval){
  let offset = {width: el.offsetWidth, height: el.offsetHeight}
  setInterval(()=>{
    let newOffset = {width: el.offsetWidth, height: el.offsetHeight}
    if(offset.height!=newOffset.height||offset.width!=newOffset.width){
      offset = newOffset
      fn()
    }
  }, interval)
}
  const typeScriptCodeContainer = document.getElementById('typeScriptCodeContainer')
  typeScriptCodeEditor = monaco.editor.create(typeScriptCodeContainer, Object.assign(editorOptions, {value: example.codeValue}))
  installResizeWatcher(typeScriptCodeContainer, typeScriptCodeEditor.layout.bind(typeScriptCodeEditor), 2000)

是的,需要每2秒钟检测一次,并确保只注册一次。我注意到monaco自动重新布局的调整大小间隔为100毫秒 - 在我看来这太频繁了。

在此处查看实际效果:https://typescript-api-playground.glitch.me/?example=2


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接