mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-18 17:38:19 +08:00
ui: allow direct edit file
This commit is contained in:
1 parent
33211aa7dc
commit
30cad9b80e
7 files changed
+100
-33
No files matched your search
@@ -10,10 +10,13 @@ export default class Dialog {
|
||||
$action: null,
|
||||
...options,
|
||||
};
|
||||
let box = '';
|
||||
if (options.width) box += `width:${options.width};max-width:${options.width};`;
|
||||
if (options.height) box += `height:${options.height};max-height:${options.height};`;
|
||||
this.$dom = $(tpl`
|
||||
<div class="dialog withBg ${this.options.classes}" style="display:none">
|
||||
<div class="dialog__content">
|
||||
<div class="dialog__body"></div>
|
||||
<div class="dialog__content" style="${box}">
|
||||
<div class="dialog__body" style="height:calc(100% - 45px);"></div>
|
||||
<div class="row"><div class="columns clearfix">
|
||||
<div class="float-right dialog__action"></div>
|
||||
</div></div>
|
||||
|
||||
@@ -9,6 +9,9 @@ function runSubstitute($container) {
|
||||
$container.find('textarea[data-yaml]').get().forEach((element) => {
|
||||
CmEditor.getOrConstruct($(element), { language: 'yaml' });
|
||||
});
|
||||
$container.find('textarea[data-plain]').get().forEach((element) => {
|
||||
CmEditor.getOrConstruct($(element), { language: 'plain' });
|
||||
});
|
||||
}
|
||||
|
||||
const cmEditorPage = new AutoloadPage('cmEditorPage', () => {
|
||||
|
||||
@@ -37,20 +37,21 @@ export default class Editor extends DOMAttachedObject {
|
||||
|
||||
async initMonaco() {
|
||||
const monaco = await import('monaco-editor/esm/vs/editor/editor.api');
|
||||
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 = $dom.val();
|
||||
const {
|
||||
onChange, language = 'markdown',
|
||||
theme = UserContext.monacoTheme || 'vs-light',
|
||||
model = `file://model-${Math.random().toString(16)}`,
|
||||
autoResize = true,
|
||||
} = this.options;
|
||||
const value = this.options.value || $dom.val();
|
||||
this.model = typeof model === 'string' ? monaco.editor.createModel(value, language, monaco.Uri.parse(model)) : model;
|
||||
const cfg = {
|
||||
theme,
|
||||
@@ -58,12 +59,25 @@ export default class Editor extends DOMAttachedObject {
|
||||
glyphMargin: true,
|
||||
lightbulb: { enabled: true },
|
||||
model: this.model,
|
||||
automaticLayout: true,
|
||||
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;
|
||||
const height = this.editor.getTopForLineNumber(lineCount + 1) + lineHeight;
|
||||
if (prevHeight !== height) {
|
||||
prevHeight = height;
|
||||
editorElement.style.height = `${height}px`;
|
||||
this.editor.layout();
|
||||
}
|
||||
};
|
||||
if (autoResize) {
|
||||
cfg.scrollbar = {
|
||||
vertical: 'hidden',
|
||||
@@ -84,23 +98,12 @@ export default class Editor extends DOMAttachedObject {
|
||||
label: 'Use light theme',
|
||||
run: () => monaco.editor.setTheme('vs-light'),
|
||||
});
|
||||
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;
|
||||
const height = this.editor.getTopForLineNumber(lineCount + 1) + lineHeight;
|
||||
if (prevHeight !== height) {
|
||||
prevHeight = height;
|
||||
editorElement.style.height = `${height}px`;
|
||||
this.editor.layout();
|
||||
}
|
||||
};
|
||||
this.editor.onDidChangeModelDecorations(() => {
|
||||
updateEditorHeight(); // typing
|
||||
requestAnimationFrame(updateEditorHeight); // folding
|
||||
});
|
||||
if (autoResize) {
|
||||
this.editor.onDidChangeModelDecorations(() => {
|
||||
updateEditorHeight(); // typing
|
||||
requestAnimationFrame(updateEditorHeight); // folding
|
||||
});
|
||||
}
|
||||
this._subscription = this.editor.onDidChangeModelContent(() => {
|
||||
const val = this.editor.getValue();
|
||||
$dom.val(val);
|
||||
@@ -109,7 +112,7 @@ export default class Editor extends DOMAttachedObject {
|
||||
});
|
||||
this.isValid = true;
|
||||
if (hasFocus) this.focus();
|
||||
updateEditorHeight();
|
||||
if (autoResize) updateEditorHeight();
|
||||
}
|
||||
|
||||
async initVditor() {
|
||||
@@ -141,10 +144,20 @@ export default class Editor extends DOMAttachedObject {
|
||||
if (hasFocus) this.focus();
|
||||
}
|
||||
|
||||
destory() {
|
||||
this.detach();
|
||||
if (this.editor?.destory) this.editor.destory();
|
||||
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);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@hydrooj/ui-default",
|
||||
"version": "4.8.10",
|
||||
"version": "4.8.11",
|
||||
"author": "undefined <i@undefined.moe>",
|
||||
"license": "AGPL-3.0",
|
||||
"main": "hydro.js",
|
||||
|
||||
@@ -4,7 +4,7 @@ import _ from 'lodash';
|
||||
import { createZipStream } from 'vj/utils/zip';
|
||||
import { NamedPage } from 'vj/misc/Page';
|
||||
import Notification from 'vj/components/notification';
|
||||
import { ConfirmDialog } from 'vj/components/dialog';
|
||||
import { ConfirmDialog, ActionDialog } from 'vj/components/dialog/index';
|
||||
import request from 'vj/utils/request';
|
||||
import pipeStream from 'vj/utils/pipeStream';
|
||||
import tpl from 'vj/utils/tpl';
|
||||
@@ -166,6 +166,54 @@ const page = new NamedPage('problem_files', () => {
|
||||
handleClickUpload(type, files);
|
||||
}
|
||||
|
||||
async function startEdit(filename, value) {
|
||||
console.log(filename, value);
|
||||
const { default: Editor } = await import('vj/components/editor/index');
|
||||
const promise = new ActionDialog({
|
||||
$body: tpl`
|
||||
<div class="typo" style="width: 100%; height: 100%">
|
||||
<textarea name="fileContent" style="width: 100%; height: 100%"></textarea>
|
||||
</div>`,
|
||||
width: `${window.innerWidth - 200}px`,
|
||||
height: `${window.innerHeight - 100}px`,
|
||||
}).open();
|
||||
const editor = new Editor($('[name="fileContent"]'), { value, autoResize: false, autoLayout: false });
|
||||
const action = await promise;
|
||||
value = editor.value();
|
||||
editor.destory();
|
||||
if (action !== 'ok') return null;
|
||||
return value;
|
||||
}
|
||||
/**
|
||||
* @param {string} type
|
||||
* @param {JQuery.ClickEvent<HTMLElement, undefined, HTMLElement, HTMLElement>} ev
|
||||
*/
|
||||
async function handleEdit(type, ev) {
|
||||
ev.preventDefault();
|
||||
const filename = ev.currentTarget.closest('[data-filename]').getAttribute('data-filename');
|
||||
const filesize = +ev.currentTarget.closest('[data-size]').getAttribute('data-size');
|
||||
const link = $(ev.currentTarget).find('a').attr('href');
|
||||
if (filesize > 8 * 1024 * 1024) Notification.error(i18n('file too large'));
|
||||
Notification.info(i18n('Loading file...'));
|
||||
const res = await request.get(link);
|
||||
const content = await request.get(res.url, undefined, { dataType: 'text' });
|
||||
const val = await startEdit(filename, content);
|
||||
console.log(val);
|
||||
if (typeof val !== 'string') return;
|
||||
Notification.info(i18n('Saving file...'));
|
||||
const data = new FormData();
|
||||
data.append('filename', filename);
|
||||
data.append('file', new Blob([val], { type: 'text/plain' }));
|
||||
data.append('type', type);
|
||||
data.append('operation', 'upload_file');
|
||||
await request.postFile('', data);
|
||||
Notification.success(i18n('File saved.'));
|
||||
await delay(2000);
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
$('.problem-files-testdata .col--name').on('click', (ev) => handleEdit('testdata', ev));
|
||||
$('.problem-files-additional_file .col--name').on('click', (ev) => handleEdit('additional_file', ev));
|
||||
$('.problem-files-testdata').on('dragover', (ev) => handleDragOver('testdata', ev));
|
||||
$('.problem-files-additional_file').on('dragover', (ev) => handleDragOver('additional_file', ev));
|
||||
$('.problem-files-testdata').on('drop', (ev) => handleDrop('testdata', ev));
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
</thead>
|
||||
<tbody>
|
||||
{%- for file in testdata -%}
|
||||
<tr data-filename="{{ file.name }}">
|
||||
<tr data-filename="{{ file.name }}" data-size="{{ file.size }}">
|
||||
<td class="col--checkbox">
|
||||
<label class="compact checkbox">
|
||||
<input type="checkbox" data-checkbox-group="testdata" data-checkbox-range>
|
||||
@@ -96,7 +96,7 @@
|
||||
</thead>
|
||||
<tbody>
|
||||
{%- for file in additional_file -%}
|
||||
<tr data-filename="{{ file.name }}">
|
||||
<tr data-filename="{{ file.name }}" data-size="{{ file.size }}">
|
||||
<td class="col--checkbox">
|
||||
<label class="compact checkbox">
|
||||
<input type="checkbox" data-checkbox-group="additional_file" data-checkbox-range>
|
||||
|
||||
@@ -22,7 +22,7 @@ const request = {
|
||||
} else if (errorThrown instanceof Error) {
|
||||
reject(errorThrown);
|
||||
} else if (typeof jqXHR.responseJSON === 'object' && jqXHR.responseJSON.error) {
|
||||
const error = jqXHR.responseJSON.error;
|
||||
const { error } = jqXHR.responseJSON;
|
||||
if (error.params) {
|
||||
const message = substitute(error.message, ...error.params);
|
||||
const err = new Error(message);
|
||||
|
||||
Reference in new issue
Block a user