mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-17 09:36:08 +08:00
ui: monaco: better auto-resize
This commit is contained in:
1 parent
6419215d91
commit
6b5b3e68d0
4 files changed
+62
-16
No files matched your search
@@ -0,0 +1,206 @@
|
||||
import _ from 'lodash';
|
||||
import DOMAttachedObject from 'vj/components/DOMAttachedObject';
|
||||
|
||||
export const config = {
|
||||
toolbar: [
|
||||
'emoji', 'headings', 'bold', 'italic', 'strike', 'link', '|',
|
||||
'list', 'ordered-list', 'check', 'outdent', 'indent', '|',
|
||||
'quote', 'line', 'code', 'inline-code', 'table', '|',
|
||||
'upload', 'edit-mode', 'fullscreen', 'export',
|
||||
],
|
||||
mode: UserContext.preferredEditorType || 'ir',
|
||||
toolbarConfig: {
|
||||
pin: true,
|
||||
},
|
||||
cdn: `${UiContext.cdn_prefix}vditor`,
|
||||
counter: {
|
||||
enable: true,
|
||||
max: 65536,
|
||||
},
|
||||
preview: {
|
||||
math: {
|
||||
inlineDigit: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
interface MonacoOptions {
|
||||
language?: string;
|
||||
onChange?: (val: string) => any;
|
||||
theme?: string;
|
||||
model?: string;
|
||||
autoResize?: boolean;
|
||||
autoLayout?: boolean;
|
||||
value?: string;
|
||||
}
|
||||
interface VditorOptions {
|
||||
theme?: 'classic' | 'dark'
|
||||
}
|
||||
type Options = MonacoOptions & VditorOptions;
|
||||
|
||||
export default class Editor extends DOMAttachedObject {
|
||||
static DOMAttachKey = 'vjEditorInstance';
|
||||
model: import('../monaco').default.editor.IModel;
|
||||
editor: import('../monaco').default.editor.IStandaloneCodeEditor;
|
||||
vditor: import('vditor').default;
|
||||
isValid: boolean;
|
||||
|
||||
constructor($dom, public options: Options = {}) {
|
||||
super($dom);
|
||||
if (UserContext.preferredEditorType === 'monaco') this.initMonaco();
|
||||
else if (options.language && options.language !== 'markdown') this.initMonaco();
|
||||
else this.initVditor();
|
||||
}
|
||||
|
||||
async initMonaco() {
|
||||
const { load } = await import('vj/components/monaco/loader');
|
||||
const { monaco, registerAction } = await load(['markdown']);
|
||||
const {
|
||||
onChange, language = 'markdown',
|
||||
theme = UserContext.monacoTheme || 'vs-light',
|
||||
model = `file://model-${Math.random().toString(16)}`,
|
||||
autoResize = true, autoLayout = true,
|
||||
} = this.options;
|
||||
const { $dom } = this;
|
||||
const hasFocus = $dom.is(':focus') || $dom.hasClass('autofocus');
|
||||
const origin = $dom.get(0);
|
||||
const ele = document.createElement('div');
|
||||
$(ele).width('100%').addClass('textbox');
|
||||
if (!autoResize && $dom.height()) $(ele).height($dom.height());
|
||||
$dom.hide();
|
||||
origin.parentElement.appendChild(ele);
|
||||
const value = this.options.value || $dom.val();
|
||||
// eslint-disable-next-line no-nested-ternary
|
||||
this.model = typeof model === 'string'
|
||||
? monaco.editor.getModel(monaco.Uri.parse(model))
|
||||
|| monaco.editor.createModel(value, language, monaco.Uri.parse(model))
|
||||
: model;
|
||||
this.model.setValue(value);
|
||||
const cfg: import('../monaco').default.editor.IStandaloneEditorConstructionOptions = {
|
||||
theme,
|
||||
lineNumbers: 'on',
|
||||
glyphMargin: true,
|
||||
lightbulb: { enabled: true },
|
||||
model: this.model,
|
||||
minimap: { enabled: false },
|
||||
hideCursorInOverviewRuler: true,
|
||||
overviewRulerLanes: 0,
|
||||
overviewRulerBorder: false,
|
||||
};
|
||||
if (autoLayout) cfg.automaticLayout = true;
|
||||
let prevHeight = 0;
|
||||
const updateEditorHeight = () => {
|
||||
const editorElement = this.editor.getDomNode();
|
||||
if (!editorElement) return;
|
||||
const lineHeight = this.editor.getOption(monaco.editor.EditorOption.lineHeight);
|
||||
const lineCount = this.editor.getModel()?.getLineCount() || 1;
|
||||
let height = this.editor.getTopForLineNumber(lineCount + 1) + lineHeight;
|
||||
if (prevHeight !== height) {
|
||||
if (window.innerHeight * 1.5 < height) {
|
||||
height = window.innerHeight;
|
||||
this.editor.updateOptions({
|
||||
scrollbar: {
|
||||
vertical: 'auto',
|
||||
horizontal: 'auto',
|
||||
handleMouseWheel: true,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
this.editor.updateOptions({
|
||||
scrollbar: {
|
||||
vertical: 'hidden',
|
||||
horizontal: 'hidden',
|
||||
handleMouseWheel: false,
|
||||
},
|
||||
});
|
||||
}
|
||||
prevHeight = height;
|
||||
editorElement.style.height = `${height}px`;
|
||||
this.editor.layout();
|
||||
}
|
||||
};
|
||||
if (autoResize) {
|
||||
cfg.wordWrap = 'bounded';
|
||||
cfg.scrollBeyondLastLine = false;
|
||||
}
|
||||
this.editor = monaco.editor.create(ele, cfg);
|
||||
registerAction(this.editor, this.model, this.$dom);
|
||||
if (autoResize) {
|
||||
this.editor.onDidChangeModelDecorations(() => {
|
||||
updateEditorHeight(); // typing
|
||||
requestAnimationFrame(updateEditorHeight); // folding
|
||||
});
|
||||
}
|
||||
this.editor.onDidChangeModelContent(() => {
|
||||
const val = this.editor.getValue();
|
||||
$dom.val(val);
|
||||
$dom.text(val);
|
||||
if (onChange) onChange(val);
|
||||
});
|
||||
this.isValid = true;
|
||||
if (hasFocus) this.focus();
|
||||
if (autoResize) updateEditorHeight();
|
||||
// @ts-ignore
|
||||
window.model = this.model;
|
||||
// @ts-ignore
|
||||
window.editor = this.editor;
|
||||
}
|
||||
|
||||
async initVditor() {
|
||||
const { default: Vditor } = await import('vditor');
|
||||
const { $dom } = this;
|
||||
const hasFocus = $dom.is(':focus') || $dom.hasClass('autofocus');
|
||||
const origin = $dom.get(0);
|
||||
const ele = document.createElement('div');
|
||||
const value = $dom.val();
|
||||
const { onChange } = this.options;
|
||||
await new Promise((resolve) => {
|
||||
this.vditor = new Vditor(ele, {
|
||||
...config,
|
||||
...this.options,
|
||||
after: () => resolve(null),
|
||||
input(v) {
|
||||
$dom.val(v);
|
||||
$dom.text(v);
|
||||
if (onChange) onChange(v);
|
||||
},
|
||||
value,
|
||||
cache: { id: Math.random().toString() },
|
||||
});
|
||||
});
|
||||
$(ele).addClass('textbox');
|
||||
$dom.hide();
|
||||
origin.parentElement.appendChild(ele);
|
||||
this.isValid = true;
|
||||
if (hasFocus) this.focus();
|
||||
}
|
||||
|
||||
destory() {
|
||||
this.detach();
|
||||
if (this.vditor?.destroy) this.vditor.destroy();
|
||||
else if (this.editor?.dispose) this.editor.dispose();
|
||||
}
|
||||
|
||||
ensureValid() {
|
||||
if (!this.isValid) throw new Error('Editor is not loaded');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string?} val
|
||||
* @returns {string}
|
||||
*/
|
||||
value(val) {
|
||||
this.ensureValid();
|
||||
if (typeof val === 'string') return this.editor.setValue(val);
|
||||
return this.editor.getValue();
|
||||
}
|
||||
|
||||
focus() {
|
||||
this.ensureValid();
|
||||
this.editor.focus();
|
||||
const range = this.model.getFullModelRange();
|
||||
this.editor.setPosition({ lineNumber: range.endLineNumber, column: range.endColumn });
|
||||
}
|
||||
}
|
||||
|
||||
_.assign(Editor, DOMAttachedObject);
|
||||
Reference in new issue
Block a user