Skip to content

Commit

Permalink
fix
Browse files Browse the repository at this point in the history
  • Loading branch information
wxiaoguang committed Oct 22, 2024
1 parent f3c7c26 commit e94463c
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 75 deletions.
79 changes: 55 additions & 24 deletions web_src/js/features/comp/ComboMarkdownEditor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,19 @@ import '@github/text-expander-element';
import $ from 'jquery';
import {attachTribute} from '../tribute.ts';
import {hideElem, showElem, autosize, isElemVisible} from '../../utils/dom.ts';
import {initEasyMDEPaste, initTextareaEvents} from './EditorUpload.ts';
import {
EventUploadStateChanged,
initEasyMDEPaste,
initTextareaEvents,
triggerUploadStateChanged,
} from './EditorUpload.ts';
import {handleGlobalEnterQuickSubmit} from './QuickSubmit.ts';
import {renderPreviewPanelContent} from '../repo-editor.ts';
import {easyMDEToolbarActions} from './EasyMDEToolbarActions.ts';
import {initTextExpander} from './TextExpander.ts';
import {showErrorToast} from '../../modules/toast.ts';
import {POST} from '../../modules/fetch.ts';
import {initTextareaMarkdown} from './EditorMarkdown.ts';
import {EventEditorContentChanged, initTextareaMarkdown, triggerEditorContentChanged} from './EditorMarkdown.ts';
import {DropzoneCustomEventReloadFiles, initDropzone} from '../dropzone.ts';

let elementIdCounter = 0;
Expand All @@ -37,7 +42,34 @@ export function validateTextareaNonEmpty(textarea) {
return true;
}

class ComboMarkdownEditor {
export class ComboMarkdownEditor {
static EventEditorContentChanged = EventEditorContentChanged;
static EventUploadStateChanged = EventUploadStateChanged;

public container : HTMLElement;

// TODO: use correct types to replace these "any" types
options: any;

tabEditor: HTMLElement;
tabPreviewer: HTMLElement;

easyMDE: any;
easyMDEToolbarActions: any;
easyMDEToolbarDefault: any;

textarea: HTMLTextAreaElement & {_giteaComboMarkdownEditor: any};
textareaMarkdownToolbar: HTMLElement;
textareaAutosize: any;

dropzone: HTMLElement;
attachedDropzoneInst: any;

previewUrl: string;
previewContext: string;
previewMode: string;
previewWiki: boolean;

constructor(container, options = {}) {
container._giteaComboMarkdownEditor = this;
this.options = options;
Expand All @@ -63,14 +95,13 @@ class ComboMarkdownEditor {

setupContainer() {
initTextExpander(this.container.querySelector('text-expander'));
this.container.addEventListener('ce-editor-content-changed', (e) => this.options?.onContentChanged?.(this, e));
}

setupTextarea() {
this.textarea = this.container.querySelector('.markdown-text-editor');
this.textarea._giteaComboMarkdownEditor = this;
this.textarea.id = `_combo_markdown_editor_${String(elementIdCounter++)}`;
this.textarea.addEventListener('input', (e) => this.options?.onContentChanged?.(this, e));
this.textarea.addEventListener('input', () => triggerEditorContentChanged(this.container));
this.applyEditorHeights(this.textarea, this.options.editorHeights);

if (this.textarea.getAttribute('data-disable-autosize') !== 'true') {
Expand Down Expand Up @@ -115,15 +146,21 @@ class ComboMarkdownEditor {

async setupDropzone() {
const dropzoneParentContainer = this.container.getAttribute('data-dropzone-parent-container');
if (dropzoneParentContainer) {
this.dropzone = this.container.closest(this.container.getAttribute('data-dropzone-parent-container'))?.querySelector('.dropzone');
if (this.dropzone) this.attachedDropzoneInst = await initDropzone(this.dropzone);
}
if (!dropzoneParentContainer) return;
this.dropzone = this.container.closest(this.container.getAttribute('data-dropzone-parent-container'))?.querySelector('.dropzone');
if (!this.dropzone) return;

this.attachedDropzoneInst = await initDropzone(this.dropzone);
// dropzone events
// * "processing" means a file is being uploaded
// * "queuecomplete" means all files have been uploaded
this.attachedDropzoneInst.on('processing', () => triggerUploadStateChanged(this.container));
this.attachedDropzoneInst.on('queuecomplete', () => triggerUploadStateChanged(this.container));
}

dropzoneGetFiles() {
if (!this.dropzone) return null;
return Array.from(this.dropzone.querySelectorAll('.files [name=files]'), (el) => el.value);
return Array.from(this.dropzone.querySelectorAll<HTMLInputElement>('.files [name=files]'), (el) => el.value);
}

dropzoneReloadFiles() {
Expand All @@ -139,11 +176,11 @@ class ComboMarkdownEditor {

isUploading() {
if (!this.dropzone) return false;
return this.attachedDropzoneInst.getUploadingFiles().length !== 0;
return this.attachedDropzoneInst.getQueuedFiles().length || this.attachedDropzoneInst.getUploadingFiles().length;
}

setupTab() {
const tabs = this.container.querySelectorAll('.tabular.menu > .item');
const tabs = this.container.querySelectorAll<HTMLElement>('.tabular.menu > .item');

// Fomantic Tab requires the "data-tab" to be globally unique.
// So here it uses our defined "data-tab-for" and "data-tab-panel" to generate the "data-tab" attribute for Fomantic.
Expand Down Expand Up @@ -175,7 +212,7 @@ class ComboMarkdownEditor {
formData.append('mode', this.previewMode);
formData.append('context', this.previewContext);
formData.append('text', this.value());
formData.append('wiki', this.previewWiki);
formData.append('wiki', String(this.previewWiki));
const response = await POST(this.previewUrl, {data: formData});
const data = await response.text();
renderPreviewPanelContent($(panelPreviewer), data);
Expand Down Expand Up @@ -242,24 +279,24 @@ class ComboMarkdownEditor {
easyMDEOpt.toolbar = this.parseEasyMDEToolbar(EasyMDE, easyMDEOpt.toolbar ?? this.easyMDEToolbarDefault);

this.easyMDE = new EasyMDE(easyMDEOpt);
this.easyMDE.codemirror.on('change', (...args) => {this.options?.onContentChanged?.(this, ...args)});
this.easyMDE.codemirror.on('change', () => triggerEditorContentChanged(this.container));
this.easyMDE.codemirror.setOption('extraKeys', {
'Cmd-Enter': (cm) => handleGlobalEnterQuickSubmit(cm.getTextArea()),
'Ctrl-Enter': (cm) => handleGlobalEnterQuickSubmit(cm.getTextArea()),
Enter: (cm) => {
const tributeContainer = document.querySelector('.tribute-container');
const tributeContainer = document.querySelector<HTMLElement>('.tribute-container');
if (!tributeContainer || tributeContainer.style.display === 'none') {
cm.execCommand('newlineAndIndent');
}
},
Up: (cm) => {
const tributeContainer = document.querySelector('.tribute-container');
const tributeContainer = document.querySelector<HTMLElement>('.tribute-container');
if (!tributeContainer || tributeContainer.style.display === 'none') {
return cm.execCommand('goLineUp');
}
},
Down: (cm) => {
const tributeContainer = document.querySelector('.tribute-container');
const tributeContainer = document.querySelector<HTMLElement>('.tribute-container');
if (!tributeContainer || tributeContainer.style.display === 'none') {
return cm.execCommand('goLineDown');
}
Expand Down Expand Up @@ -319,13 +356,7 @@ export function getComboMarkdownEditor(el) {
return el?._giteaComboMarkdownEditor;
}

export async function initComboMarkdownEditor(container, options = {}) {
if (container instanceof $) {
if (container.length !== 1) {
throw new Error('initComboMarkdownEditor: container must be a single element');
}
container = container[0];
}
export async function initComboMarkdownEditor(container: HTMLElement, options = {}) {
if (!container) {
throw new Error('initComboMarkdownEditor: container is null');
}
Expand Down
4 changes: 3 additions & 1 deletion web_src/js/features/comp/EditorMarkdown.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
export const EventEditorContentChanged = 'ce-editor-content-changed';

export function triggerEditorContentChanged(target) {
target.dispatchEvent(new CustomEvent('ce-editor-content-changed', {bubbles: true}));
target.dispatchEvent(new CustomEvent(EventEditorContentChanged, {bubbles: true}));
}

function handleIndentSelection(textarea, e) {
Expand Down
21 changes: 15 additions & 6 deletions web_src/js/features/comp/EditorUpload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,35 @@ import {
DropzoneCustomEventUploadDone,
generateMarkdownLinkForAttachment,
} from '../dropzone.ts';
import type CodeMirror from 'codemirror';

let uploadIdCounter = 0;

function uploadFile(dropzoneEl, file, placeholderCallback) {
export const EventUploadStateChanged = 'ce-upload-state-changed';

export function triggerUploadStateChanged(target) {
target.dispatchEvent(new CustomEvent(EventUploadStateChanged, {bubbles: true}));
}

function uploadFile(dropzoneEl, file) {
return new Promise((resolve) => {
const curUploadId = uploadIdCounter++;
file._giteaUploadId = curUploadId;
const dropzoneInst = dropzoneEl.dropzone;
const onUploadDone = ({file}) => {
if (file._giteaUploadId === curUploadId) {
dropzoneInst.off(DropzoneCustomEventUploadDone, onUploadDone);
resolve();
resolve(file);
}
};
dropzoneInst.on(DropzoneCustomEventUploadDone, onUploadDone);
dropzoneInst.handleFiles([file]);
// if there is no setTimeout, then ComboMarkdownEditor.isUploading() does not working correctly
setTimeout(() => placeholderCallback(), 0);
});
}

class TextareaEditor {
editor : HTMLTextAreaElement;

constructor(editor) {
this.editor = editor;
}
Expand Down Expand Up @@ -63,6 +70,8 @@ class TextareaEditor {
}

class CodeMirrorEditor {
editor: CodeMirror.EditorFromTextArea;

constructor(editor) {
this.editor = editor;
}
Expand Down Expand Up @@ -101,8 +110,8 @@ async function handleUploadFiles(editor, dropzoneEl, files, e) {
const {width, dppx} = await imageInfo(file);
const placeholder = `[${name}](uploading ...)`;

// the "file" will get its "uuid" during the upload
await uploadFile(dropzoneEl, file, () => editor.insertPlaceholder(placeholder));
editor.insertPlaceholder(placeholder);
await uploadFile(dropzoneEl, file); // the "file" will get its "uuid" during the upload
editor.replacePlaceholder(placeholder, generateMarkdownLinkForAttachment(file, {width, dppx}));
}
}
Expand Down
24 changes: 9 additions & 15 deletions web_src/js/features/repo-issue-edit.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import $ from 'jquery';
import {handleReply} from './repo-issue.ts';
import {getComboMarkdownEditor, initComboMarkdownEditor} from './comp/ComboMarkdownEditor.ts';
import {getComboMarkdownEditor, initComboMarkdownEditor, ComboMarkdownEditor} from './comp/ComboMarkdownEditor.ts';
import {POST} from '../modules/fetch.ts';
import {showErrorToast} from '../modules/toast.ts';
import {hideElem, showElem} from '../utils/dom.ts';
import {attachRefIssueContextPopup} from './contextpopup.ts';
import {initCommentContent, initMarkupContent} from '../markup/content.ts';
import {triggerUploadStateChanged} from './comp/EditorUpload.ts';

async function onEditContent(event) {
event.preventDefault();
Expand All @@ -15,7 +16,7 @@ async function onEditContent(event) {
const renderContent = segment.querySelector('.render-content');
const rawContent = segment.querySelector('.raw-content');

let comboMarkdownEditor;
let comboMarkdownEditor : ComboMarkdownEditor;

const cancelAndReset = (e) => {
e.preventDefault();
Expand Down Expand Up @@ -78,21 +79,13 @@ async function onEditContent(event) {

comboMarkdownEditor = getComboMarkdownEditor(editContentZone.querySelector('.combo-markdown-editor'));
if (!comboMarkdownEditor) {
const opts = {};
opts.onContentChanged = (editor) => {
const saveButton = editContentZone.querySelector('.ui.primary.button');
const isUploading = editor.isUploading();
if (saveButton) {
saveButton.disabled = isUploading;
}
};

editContentZone.innerHTML = document.querySelector('#issue-comment-editor-template').innerHTML;
comboMarkdownEditor = await initComboMarkdownEditor(editContentZone.querySelector('.combo-markdown-editor'), opts);

const saveButton = editContentZone.querySelector('.ui.primary.button');
comboMarkdownEditor = await initComboMarkdownEditor(editContentZone.querySelector('.combo-markdown-editor'));
const syncUiState = () => saveButton.disabled = comboMarkdownEditor.isUploading();
comboMarkdownEditor.container.addEventListener(ComboMarkdownEditor.EventUploadStateChanged, syncUiState);
editContentZone.querySelector('.ui.cancel.button').addEventListener('click', cancelAndReset);
editContentZone.querySelector('.ui.primary.button').addEventListener('click', saveAndRefresh);
opts.onContentChanged(comboMarkdownEditor);
saveButton.addEventListener('click', saveAndRefresh);
}

// Show write/preview tab and copy raw content as needed
Expand All @@ -104,6 +97,7 @@ async function onEditContent(event) {
}
comboMarkdownEditor.switchTabToEditor();
comboMarkdownEditor.focus();
triggerUploadStateChanged(comboMarkdownEditor.container);
}

export function initRepoIssueCommentEdit() {
Expand Down
48 changes: 22 additions & 26 deletions web_src/js/features/repo-issue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {htmlEscape} from 'escape-goat';
import {createTippy, showTemporaryTooltip} from '../modules/tippy.ts';
import {hideElem, showElem, toggleElem} from '../utils/dom.ts';
import {setFileFolding} from './file-fold.ts';
import {getComboMarkdownEditor, initComboMarkdownEditor} from './comp/ComboMarkdownEditor.ts';
import {ComboMarkdownEditor, getComboMarkdownEditor, initComboMarkdownEditor} from './comp/ComboMarkdownEditor.ts';
import {toAbsoluteUrl} from '../utils.ts';
import {GET, POST} from '../modules/fetch.ts';
import {showErrorToast} from '../modules/toast.ts';
Expand Down Expand Up @@ -483,9 +483,9 @@ export function initRepoPullRequestReview() {
await handleReply(this);
});

const $reviewBox = $('.review-box-panel');
if ($reviewBox.length === 1) {
const _promise = initComboMarkdownEditor($reviewBox.find('.combo-markdown-editor'));
const elReviewBox = document.querySelector('.review-box-panel');
if (elReviewBox) {
initComboMarkdownEditor(elReviewBox.querySelector('.combo-markdown-editor'));
}

// The following part is only for diff views
Expand Down Expand Up @@ -548,7 +548,7 @@ export function initRepoPullRequestReview() {
$td.find("input[name='line']").val(idx);
$td.find("input[name='side']").val(side === 'left' ? 'previous' : 'proposed');
$td.find("input[name='path']").val(path);
const editor = await initComboMarkdownEditor($td.find('.combo-markdown-editor'));
const editor = await initComboMarkdownEditor($td[0].querySelector('.combo-markdown-editor'));
editor.focus();
} catch (error) {
console.error(error);
Expand Down Expand Up @@ -669,13 +669,11 @@ export async function initSingleCommentEditor($commentForm) {
// pages:
// * normal new issue/pr page: no status-button, no comment-button (there is only a normal submit button which can submit empty content)
// * issue/pr view page: with comment form, has status-button and comment-button
const opts = {};
const statusButton = document.querySelector('#status-button');
const commentButton = document.querySelector('#comment-button');
opts.onContentChanged = (editor) => {
const editorText = editor.value().trim();
const isUploading = editor.isUploading();

const editor = await initComboMarkdownEditor($commentForm[0].querySelector('.combo-markdown-editor'));
const statusButton = document.querySelector<HTMLButtonElement>('#status-button');
const commentButton = document.querySelector<HTMLButtonElement>('#comment-button');
const syncUiState = () => {
const editorText = editor.value().trim(), isUploading = editor.isUploading();
if (statusButton) {
statusButton.textContent = statusButton.getAttribute(editorText ? 'data-status-and-comment' : 'data-status');
statusButton.disabled = isUploading;
Expand All @@ -684,25 +682,23 @@ export async function initSingleCommentEditor($commentForm) {
commentButton.disabled = !editorText || isUploading;
}
};
const editor = await initComboMarkdownEditor($commentForm.find('.combo-markdown-editor'), opts);
opts.onContentChanged(editor); // sync state of buttons with the initial content
editor.container.addEventListener(ComboMarkdownEditor.EventUploadStateChanged, syncUiState);
editor.container.addEventListener(ComboMarkdownEditor.EventEditorContentChanged, syncUiState);
syncUiState();
}

export function initIssueTemplateCommentEditors($commentForm) {
// pages:
// * new issue with issue template
const $comboFields = $commentForm.find('.combo-editor-dropzone');

const initCombo = async ($combo) => {
const $dropzoneContainer = $combo.find('.form-field-dropzone');
const $formField = $combo.find('.form-field-real');
const $markdownEditor = $combo.find('.combo-markdown-editor');
const initCombo = async (elCombo) => {
const $formField = $(elCombo.querySelector('.form-field-real'));
const dropzoneContainer = elCombo.querySelector('.form-field-dropzone');
const markdownEditor = elCombo.querySelector('.combo-markdown-editor');

const editor = await initComboMarkdownEditor($markdownEditor, {
onContentChanged: (editor) => {
$formField.val(editor.value());
},
});
const editor = await initComboMarkdownEditor(markdownEditor);
editor.container.addEventListener(ComboMarkdownEditor.EventEditorContentChanged, () => $formField.val(editor.value()));

$formField.on('focus', async () => {
// deactivate all markdown editors
Expand All @@ -712,16 +708,16 @@ export function initIssueTemplateCommentEditors($commentForm) {

// activate this markdown editor
hideElem($formField);
showElem($markdownEditor);
showElem($dropzoneContainer);
showElem(markdownEditor);
showElem(dropzoneContainer);

await editor.switchToUserPreference();
editor.focus();
});
};

for (const el of $comboFields) {
initCombo($(el));
initCombo(el);
}
}

Expand Down
Loading

0 comments on commit e94463c

Please sign in to comment.