@@ -3,14 +3,19 @@ import '@github/text-expander-element';
3
3
import $ from 'jquery' ;
4
4
import { attachTribute } from '../tribute.ts' ;
5
5
import { hideElem , showElem , autosize , isElemVisible } from '../../utils/dom.ts' ;
6
- import { initEasyMDEPaste , initTextareaEvents } from './EditorUpload.ts' ;
6
+ import {
7
+ EventUploadStateChanged ,
8
+ initEasyMDEPaste ,
9
+ initTextareaEvents ,
10
+ triggerUploadStateChanged ,
11
+ } from './EditorUpload.ts' ;
7
12
import { handleGlobalEnterQuickSubmit } from './QuickSubmit.ts' ;
8
13
import { renderPreviewPanelContent } from '../repo-editor.ts' ;
9
14
import { easyMDEToolbarActions } from './EasyMDEToolbarActions.ts' ;
10
15
import { initTextExpander } from './TextExpander.ts' ;
11
16
import { showErrorToast } from '../../modules/toast.ts' ;
12
17
import { POST } from '../../modules/fetch.ts' ;
13
- import { initTextareaMarkdown } from './EditorMarkdown.ts' ;
18
+ import { EventEditorContentChanged , initTextareaMarkdown , triggerEditorContentChanged } from './EditorMarkdown.ts' ;
14
19
import { DropzoneCustomEventReloadFiles , initDropzone } from '../dropzone.ts' ;
15
20
16
21
let elementIdCounter = 0 ;
@@ -37,7 +42,34 @@ export function validateTextareaNonEmpty(textarea) {
37
42
return true ;
38
43
}
39
44
40
- class ComboMarkdownEditor {
45
+ export class ComboMarkdownEditor {
46
+ static EventEditorContentChanged = EventEditorContentChanged ;
47
+ static EventUploadStateChanged = EventUploadStateChanged ;
48
+
49
+ public container : HTMLElement ;
50
+
51
+ // TODO: use correct types to replace these "any" types
52
+ options : any ;
53
+
54
+ tabEditor : HTMLElement ;
55
+ tabPreviewer : HTMLElement ;
56
+
57
+ easyMDE : any ;
58
+ easyMDEToolbarActions : any ;
59
+ easyMDEToolbarDefault : any ;
60
+
61
+ textarea : HTMLTextAreaElement & { _giteaComboMarkdownEditor : any } ;
62
+ textareaMarkdownToolbar : HTMLElement ;
63
+ textareaAutosize : any ;
64
+
65
+ dropzone : HTMLElement ;
66
+ attachedDropzoneInst : any ;
67
+
68
+ previewUrl : string ;
69
+ previewContext : string ;
70
+ previewMode : string ;
71
+ previewWiki : boolean ;
72
+
41
73
constructor ( container , options = { } ) {
42
74
container . _giteaComboMarkdownEditor = this ;
43
75
this . options = options ;
@@ -63,14 +95,13 @@ class ComboMarkdownEditor {
63
95
64
96
setupContainer ( ) {
65
97
initTextExpander ( this . container . querySelector ( 'text-expander' ) ) ;
66
- this . container . addEventListener ( 'ce-editor-content-changed' , ( e ) => this . options ?. onContentChanged ?.( this , e ) ) ;
67
98
}
68
99
69
100
setupTextarea ( ) {
70
101
this . textarea = this . container . querySelector ( '.markdown-text-editor' ) ;
71
102
this . textarea . _giteaComboMarkdownEditor = this ;
72
103
this . textarea . id = `_combo_markdown_editor_${ String ( elementIdCounter ++ ) } ` ;
73
- this . textarea . addEventListener ( 'input' , ( e ) => this . options ?. onContentChanged ?. ( this , e ) ) ;
104
+ this . textarea . addEventListener ( 'input' , ( ) => triggerEditorContentChanged ( this . container ) ) ;
74
105
this . applyEditorHeights ( this . textarea , this . options . editorHeights ) ;
75
106
76
107
if ( this . textarea . getAttribute ( 'data-disable-autosize' ) !== 'true' ) {
@@ -115,15 +146,21 @@ class ComboMarkdownEditor {
115
146
116
147
async setupDropzone ( ) {
117
148
const dropzoneParentContainer = this . container . getAttribute ( 'data-dropzone-parent-container' ) ;
118
- if ( dropzoneParentContainer ) {
119
- this . dropzone = this . container . closest ( this . container . getAttribute ( 'data-dropzone-parent-container' ) ) ?. querySelector ( '.dropzone' ) ;
120
- if ( this . dropzone ) this . attachedDropzoneInst = await initDropzone ( this . dropzone ) ;
121
- }
149
+ if ( ! dropzoneParentContainer ) return ;
150
+ this . dropzone = this . container . closest ( this . container . getAttribute ( 'data-dropzone-parent-container' ) ) ?. querySelector ( '.dropzone' ) ;
151
+ if ( ! this . dropzone ) return ;
152
+
153
+ this . attachedDropzoneInst = await initDropzone ( this . dropzone ) ;
154
+ // dropzone events
155
+ // * "processing" means a file is being uploaded
156
+ // * "queuecomplete" means all files have been uploaded
157
+ this . attachedDropzoneInst . on ( 'processing' , ( ) => triggerUploadStateChanged ( this . container ) ) ;
158
+ this . attachedDropzoneInst . on ( 'queuecomplete' , ( ) => triggerUploadStateChanged ( this . container ) ) ;
122
159
}
123
160
124
161
dropzoneGetFiles ( ) {
125
162
if ( ! this . dropzone ) return null ;
126
- return Array . from ( this . dropzone . querySelectorAll ( '.files [name=files]' ) , ( el ) => el . value ) ;
163
+ return Array . from ( this . dropzone . querySelectorAll < HTMLInputElement > ( '.files [name=files]' ) , ( el ) => el . value ) ;
127
164
}
128
165
129
166
dropzoneReloadFiles ( ) {
@@ -137,8 +174,13 @@ class ComboMarkdownEditor {
137
174
this . attachedDropzoneInst . emit ( DropzoneCustomEventReloadFiles ) ;
138
175
}
139
176
177
+ isUploading ( ) {
178
+ if ( ! this . dropzone ) return false ;
179
+ return this . attachedDropzoneInst . getQueuedFiles ( ) . length || this . attachedDropzoneInst . getUploadingFiles ( ) . length ;
180
+ }
181
+
140
182
setupTab ( ) {
141
- const tabs = this . container . querySelectorAll ( '.tabular.menu > .item' ) ;
183
+ const tabs = this . container . querySelectorAll < HTMLElement > ( '.tabular.menu > .item' ) ;
142
184
143
185
// Fomantic Tab requires the "data-tab" to be globally unique.
144
186
// 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 {
170
212
formData . append ( 'mode' , this . previewMode ) ;
171
213
formData . append ( 'context' , this . previewContext ) ;
172
214
formData . append ( 'text' , this . value ( ) ) ;
173
- formData . append ( 'wiki' , this . previewWiki ) ;
215
+ formData . append ( 'wiki' , String ( this . previewWiki ) ) ;
174
216
const response = await POST ( this . previewUrl , { data : formData } ) ;
175
217
const data = await response . text ( ) ;
176
218
renderPreviewPanelContent ( $ ( panelPreviewer ) , data ) ;
@@ -237,24 +279,24 @@ class ComboMarkdownEditor {
237
279
easyMDEOpt . toolbar = this . parseEasyMDEToolbar ( EasyMDE , easyMDEOpt . toolbar ?? this . easyMDEToolbarDefault ) ;
238
280
239
281
this . easyMDE = new EasyMDE ( easyMDEOpt ) ;
240
- this . easyMDE . codemirror . on ( 'change' , ( ... args ) => { this . options ?. onContentChanged ?. ( this , ... args ) } ) ;
282
+ this . easyMDE . codemirror . on ( 'change' , ( ) => triggerEditorContentChanged ( this . container ) ) ;
241
283
this . easyMDE . codemirror . setOption ( 'extraKeys' , {
242
284
'Cmd-Enter' : ( cm ) => handleGlobalEnterQuickSubmit ( cm . getTextArea ( ) ) ,
243
285
'Ctrl-Enter' : ( cm ) => handleGlobalEnterQuickSubmit ( cm . getTextArea ( ) ) ,
244
286
Enter : ( cm ) => {
245
- const tributeContainer = document . querySelector ( '.tribute-container' ) ;
287
+ const tributeContainer = document . querySelector < HTMLElement > ( '.tribute-container' ) ;
246
288
if ( ! tributeContainer || tributeContainer . style . display === 'none' ) {
247
289
cm . execCommand ( 'newlineAndIndent' ) ;
248
290
}
249
291
} ,
250
292
Up : ( cm ) => {
251
- const tributeContainer = document . querySelector ( '.tribute-container' ) ;
293
+ const tributeContainer = document . querySelector < HTMLElement > ( '.tribute-container' ) ;
252
294
if ( ! tributeContainer || tributeContainer . style . display === 'none' ) {
253
295
return cm . execCommand ( 'goLineUp' ) ;
254
296
}
255
297
} ,
256
298
Down : ( cm ) => {
257
- const tributeContainer = document . querySelector ( '.tribute-container' ) ;
299
+ const tributeContainer = document . querySelector < HTMLElement > ( '.tribute-container' ) ;
258
300
if ( ! tributeContainer || tributeContainer . style . display === 'none' ) {
259
301
return cm . execCommand ( 'goLineDown' ) ;
260
302
}
@@ -314,13 +356,7 @@ export function getComboMarkdownEditor(el) {
314
356
return el ?. _giteaComboMarkdownEditor ;
315
357
}
316
358
317
- export async function initComboMarkdownEditor ( container , options = { } ) {
318
- if ( container instanceof $ ) {
319
- if ( container . length !== 1 ) {
320
- throw new Error ( 'initComboMarkdownEditor: container must be a single element' ) ;
321
- }
322
- container = container [ 0 ] ;
323
- }
359
+ export async function initComboMarkdownEditor ( container : HTMLElement , options = { } ) {
324
360
if ( ! container ) {
325
361
throw new Error ( 'initComboMarkdownEditor: container is null' ) ;
326
362
}
0 commit comments