aboutsummaryrefslogtreecommitdiffstats
path: root/web_src/js/features/comp/ComboMarkdownEditor.ts
diff options
context:
space:
mode:
Diffstat (limited to 'web_src/js/features/comp/ComboMarkdownEditor.ts')
-rw-r--r--web_src/js/features/comp/ComboMarkdownEditor.ts82
1 files changed, 59 insertions, 23 deletions
diff --git a/web_src/js/features/comp/ComboMarkdownEditor.ts b/web_src/js/features/comp/ComboMarkdownEditor.ts
index 5f1807f373..d0e122c54a 100644
--- a/web_src/js/features/comp/ComboMarkdownEditor.ts
+++ b/web_src/js/features/comp/ComboMarkdownEditor.ts
@@ -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;
@@ -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;
@@ -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') {
@@ -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() {
@@ -137,8 +174,13 @@ class ComboMarkdownEditor {
this.attachedDropzoneInst.emit(DropzoneCustomEventReloadFiles);
}
+ isUploading() {
+ if (!this.dropzone) return false;
+ 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.
@@ -170,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);
@@ -237,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');
}
@@ -314,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');
}