Enable Typescript noImplicitAny
(#33322)
Enable `noImplicitAny` and fix all issues. --------- Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
parent
6fe4d1c038
commit
c7f4ca2653
63 changed files with 326 additions and 270 deletions
|
@ -29,10 +29,10 @@ let elementIdCounter = 0;
|
|||
|
||||
/**
|
||||
* validate if the given textarea is non-empty.
|
||||
* @param {HTMLElement} textarea - The textarea element to be validated.
|
||||
* @param {HTMLTextAreaElement} textarea - The textarea element to be validated.
|
||||
* @returns {boolean} returns true if validation succeeded.
|
||||
*/
|
||||
export function validateTextareaNonEmpty(textarea) {
|
||||
export function validateTextareaNonEmpty(textarea: HTMLTextAreaElement) {
|
||||
// When using EasyMDE, the original edit area HTML element is hidden, breaking HTML5 input validation.
|
||||
// The workaround (https://github.com/sparksuite/simplemde-markdown-editor/issues/324) doesn't work with contenteditable, so we just show an alert.
|
||||
if (!textarea.value) {
|
||||
|
@ -49,16 +49,25 @@ export function validateTextareaNonEmpty(textarea) {
|
|||
return true;
|
||||
}
|
||||
|
||||
type Heights = {
|
||||
minHeight?: string,
|
||||
height?: string,
|
||||
maxHeight?: string,
|
||||
};
|
||||
|
||||
type ComboMarkdownEditorOptions = {
|
||||
editorHeights?: {minHeight?: string, height?: string, maxHeight?: string},
|
||||
editorHeights?: Heights,
|
||||
easyMDEOptions?: EasyMDE.Options,
|
||||
};
|
||||
|
||||
type ComboMarkdownEditorTextarea = HTMLTextAreaElement & {_giteaComboMarkdownEditor: any};
|
||||
type ComboMarkdownEditorContainer = HTMLElement & {_giteaComboMarkdownEditor?: any};
|
||||
|
||||
export class ComboMarkdownEditor {
|
||||
static EventEditorContentChanged = EventEditorContentChanged;
|
||||
static EventUploadStateChanged = EventUploadStateChanged;
|
||||
|
||||
public container : HTMLElement;
|
||||
public container: HTMLElement;
|
||||
|
||||
options: ComboMarkdownEditorOptions;
|
||||
|
||||
|
@ -70,7 +79,7 @@ export class ComboMarkdownEditor {
|
|||
easyMDEToolbarActions: any;
|
||||
easyMDEToolbarDefault: any;
|
||||
|
||||
textarea: HTMLTextAreaElement & {_giteaComboMarkdownEditor: any};
|
||||
textarea: ComboMarkdownEditorTextarea;
|
||||
textareaMarkdownToolbar: HTMLElement;
|
||||
textareaAutosize: any;
|
||||
|
||||
|
@ -81,7 +90,7 @@ export class ComboMarkdownEditor {
|
|||
previewUrl: string;
|
||||
previewContext: string;
|
||||
|
||||
constructor(container, options:ComboMarkdownEditorOptions = {}) {
|
||||
constructor(container: ComboMarkdownEditorContainer, options:ComboMarkdownEditorOptions = {}) {
|
||||
if (container._giteaComboMarkdownEditor) throw new Error('ComboMarkdownEditor already initialized');
|
||||
container._giteaComboMarkdownEditor = this;
|
||||
this.options = options;
|
||||
|
@ -98,7 +107,7 @@ export class ComboMarkdownEditor {
|
|||
await this.switchToUserPreference();
|
||||
}
|
||||
|
||||
applyEditorHeights(el, heights) {
|
||||
applyEditorHeights(el: HTMLElement, heights: Heights) {
|
||||
if (!heights) return;
|
||||
if (heights.minHeight) el.style.minHeight = heights.minHeight;
|
||||
if (heights.height) el.style.height = heights.height;
|
||||
|
@ -283,7 +292,7 @@ export class ComboMarkdownEditor {
|
|||
];
|
||||
}
|
||||
|
||||
parseEasyMDEToolbar(easyMde: typeof EasyMDE, actions) {
|
||||
parseEasyMDEToolbar(easyMde: typeof EasyMDE, actions: any) {
|
||||
this.easyMDEToolbarActions = this.easyMDEToolbarActions || easyMDEToolbarActions(easyMde, this);
|
||||
const processed = [];
|
||||
for (const action of actions) {
|
||||
|
@ -332,21 +341,21 @@ export class ComboMarkdownEditor {
|
|||
this.easyMDE = new EasyMDE(easyMDEOpt);
|
||||
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) => {
|
||||
'Cmd-Enter': (cm: any) => handleGlobalEnterQuickSubmit(cm.getTextArea()),
|
||||
'Ctrl-Enter': (cm: any) => handleGlobalEnterQuickSubmit(cm.getTextArea()),
|
||||
Enter: (cm: any) => {
|
||||
const tributeContainer = document.querySelector<HTMLElement>('.tribute-container');
|
||||
if (!tributeContainer || tributeContainer.style.display === 'none') {
|
||||
cm.execCommand('newlineAndIndent');
|
||||
}
|
||||
},
|
||||
Up: (cm) => {
|
||||
Up: (cm: any) => {
|
||||
const tributeContainer = document.querySelector<HTMLElement>('.tribute-container');
|
||||
if (!tributeContainer || tributeContainer.style.display === 'none') {
|
||||
return cm.execCommand('goLineUp');
|
||||
}
|
||||
},
|
||||
Down: (cm) => {
|
||||
Down: (cm: any) => {
|
||||
const tributeContainer = document.querySelector<HTMLElement>('.tribute-container');
|
||||
if (!tributeContainer || tributeContainer.style.display === 'none') {
|
||||
return cm.execCommand('goLineDown');
|
||||
|
@ -354,14 +363,14 @@ export class ComboMarkdownEditor {
|
|||
},
|
||||
});
|
||||
this.applyEditorHeights(this.container.querySelector('.CodeMirror-scroll'), this.options.editorHeights);
|
||||
await attachTribute(this.easyMDE.codemirror.getInputField(), {mentions: true, emoji: true});
|
||||
await attachTribute(this.easyMDE.codemirror.getInputField());
|
||||
if (this.dropzone) {
|
||||
initEasyMDEPaste(this.easyMDE, this.dropzone);
|
||||
}
|
||||
hideElem(this.textareaMarkdownToolbar);
|
||||
}
|
||||
|
||||
value(v = undefined) {
|
||||
value(v: any = undefined) {
|
||||
if (v === undefined) {
|
||||
if (this.easyMDE) {
|
||||
return this.easyMDE.value();
|
||||
|
@ -402,7 +411,7 @@ export class ComboMarkdownEditor {
|
|||
}
|
||||
}
|
||||
|
||||
export function getComboMarkdownEditor(el) {
|
||||
export function getComboMarkdownEditor(el: any) {
|
||||
if (!el) return null;
|
||||
if (el.length) el = el[0];
|
||||
return el._giteaComboMarkdownEditor;
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
export const EventEditorContentChanged = 'ce-editor-content-changed';
|
||||
|
||||
export function triggerEditorContentChanged(target) {
|
||||
export function triggerEditorContentChanged(target: HTMLElement) {
|
||||
target.dispatchEvent(new CustomEvent(EventEditorContentChanged, {bubbles: true}));
|
||||
}
|
||||
|
||||
export function textareaInsertText(textarea, value) {
|
||||
export function textareaInsertText(textarea: HTMLTextAreaElement, value: string) {
|
||||
const startPos = textarea.selectionStart;
|
||||
const endPos = textarea.selectionEnd;
|
||||
textarea.value = textarea.value.substring(0, startPos) + value + textarea.value.substring(endPos);
|
||||
|
@ -20,7 +20,7 @@ type TextareaValueSelection = {
|
|||
selEnd: number;
|
||||
}
|
||||
|
||||
function handleIndentSelection(textarea: HTMLTextAreaElement, e) {
|
||||
function handleIndentSelection(textarea: HTMLTextAreaElement, e: KeyboardEvent) {
|
||||
const selStart = textarea.selectionStart;
|
||||
const selEnd = textarea.selectionEnd;
|
||||
if (selEnd === selStart) return; // do not process when no selection
|
||||
|
@ -188,7 +188,7 @@ function isTextExpanderShown(textarea: HTMLElement): boolean {
|
|||
return Boolean(textarea.closest('text-expander')?.querySelector('.suggestions'));
|
||||
}
|
||||
|
||||
export function initTextareaMarkdown(textarea) {
|
||||
export function initTextareaMarkdown(textarea: HTMLTextAreaElement) {
|
||||
textarea.addEventListener('keydown', (e) => {
|
||||
if (isTextExpanderShown(textarea)) return;
|
||||
if (e.key === 'Tab' && !e.ctrlKey && !e.metaKey && !e.altKey) {
|
||||
|
|
|
@ -8,43 +8,46 @@ import {
|
|||
generateMarkdownLinkForAttachment,
|
||||
} from '../dropzone.ts';
|
||||
import type CodeMirror from 'codemirror';
|
||||
import type EasyMDE from 'easymde';
|
||||
import type {DropzoneFile} from 'dropzone';
|
||||
|
||||
let uploadIdCounter = 0;
|
||||
|
||||
export const EventUploadStateChanged = 'ce-upload-state-changed';
|
||||
|
||||
export function triggerUploadStateChanged(target) {
|
||||
export function triggerUploadStateChanged(target: HTMLElement) {
|
||||
target.dispatchEvent(new CustomEvent(EventUploadStateChanged, {bubbles: true}));
|
||||
}
|
||||
|
||||
function uploadFile(dropzoneEl, file) {
|
||||
function uploadFile(dropzoneEl: HTMLElement, file: File) {
|
||||
return new Promise((resolve) => {
|
||||
const curUploadId = uploadIdCounter++;
|
||||
file._giteaUploadId = curUploadId;
|
||||
(file as any)._giteaUploadId = curUploadId;
|
||||
const dropzoneInst = dropzoneEl.dropzone;
|
||||
const onUploadDone = ({file}) => {
|
||||
const onUploadDone = ({file}: {file: any}) => {
|
||||
if (file._giteaUploadId === curUploadId) {
|
||||
dropzoneInst.off(DropzoneCustomEventUploadDone, onUploadDone);
|
||||
resolve(file);
|
||||
}
|
||||
};
|
||||
dropzoneInst.on(DropzoneCustomEventUploadDone, onUploadDone);
|
||||
dropzoneInst.handleFiles([file]);
|
||||
// FIXME: this is not entirely correct because `file` does not satisfy DropzoneFile (we have abused the Dropzone for long time)
|
||||
dropzoneInst.addFile(file as DropzoneFile);
|
||||
});
|
||||
}
|
||||
|
||||
class TextareaEditor {
|
||||
editor : HTMLTextAreaElement;
|
||||
editor: HTMLTextAreaElement;
|
||||
|
||||
constructor(editor) {
|
||||
constructor(editor: HTMLTextAreaElement) {
|
||||
this.editor = editor;
|
||||
}
|
||||
|
||||
insertPlaceholder(value) {
|
||||
insertPlaceholder(value: string) {
|
||||
textareaInsertText(this.editor, value);
|
||||
}
|
||||
|
||||
replacePlaceholder(oldVal, newVal) {
|
||||
replacePlaceholder(oldVal: string, newVal: string) {
|
||||
const editor = this.editor;
|
||||
const startPos = editor.selectionStart;
|
||||
const endPos = editor.selectionEnd;
|
||||
|
@ -65,11 +68,11 @@ class TextareaEditor {
|
|||
class CodeMirrorEditor {
|
||||
editor: CodeMirror.EditorFromTextArea;
|
||||
|
||||
constructor(editor) {
|
||||
constructor(editor: CodeMirror.EditorFromTextArea) {
|
||||
this.editor = editor;
|
||||
}
|
||||
|
||||
insertPlaceholder(value) {
|
||||
insertPlaceholder(value: string) {
|
||||
const editor = this.editor;
|
||||
const startPoint = editor.getCursor('start');
|
||||
const endPoint = editor.getCursor('end');
|
||||
|
@ -80,7 +83,7 @@ class CodeMirrorEditor {
|
|||
triggerEditorContentChanged(editor.getTextArea());
|
||||
}
|
||||
|
||||
replacePlaceholder(oldVal, newVal) {
|
||||
replacePlaceholder(oldVal: string, newVal: string) {
|
||||
const editor = this.editor;
|
||||
const endPoint = editor.getCursor('end');
|
||||
if (editor.getSelection() === oldVal) {
|
||||
|
@ -96,7 +99,7 @@ class CodeMirrorEditor {
|
|||
}
|
||||
}
|
||||
|
||||
async function handleUploadFiles(editor, dropzoneEl, files, e) {
|
||||
async function handleUploadFiles(editor: CodeMirrorEditor | TextareaEditor, dropzoneEl: HTMLElement, files: Array<File> | FileList, e: Event) {
|
||||
e.preventDefault();
|
||||
for (const file of files) {
|
||||
const name = file.name.slice(0, file.name.lastIndexOf('.'));
|
||||
|
@ -109,13 +112,13 @@ async function handleUploadFiles(editor, dropzoneEl, files, e) {
|
|||
}
|
||||
}
|
||||
|
||||
export function removeAttachmentLinksFromMarkdown(text, fileUuid) {
|
||||
export function removeAttachmentLinksFromMarkdown(text: string, fileUuid: string) {
|
||||
text = text.replace(new RegExp(`!?\\[([^\\]]+)\\]\\(/?attachments/${fileUuid}\\)`, 'g'), '');
|
||||
text = text.replace(new RegExp(`<img[^>]+src="/?attachments/${fileUuid}"[^>]*>`, 'g'), '');
|
||||
return text;
|
||||
}
|
||||
|
||||
function handleClipboardText(textarea, e, {text, isShiftDown}) {
|
||||
function handleClipboardText(textarea: HTMLTextAreaElement, e: ClipboardEvent, text: string, isShiftDown: boolean) {
|
||||
// pasting with "shift" means "paste as original content" in most applications
|
||||
if (isShiftDown) return; // let the browser handle it
|
||||
|
||||
|
@ -131,7 +134,7 @@ function handleClipboardText(textarea, e, {text, isShiftDown}) {
|
|||
}
|
||||
|
||||
// extract text and images from "paste" event
|
||||
function getPastedContent(e) {
|
||||
function getPastedContent(e: ClipboardEvent) {
|
||||
const images = [];
|
||||
for (const item of e.clipboardData?.items ?? []) {
|
||||
if (item.type?.startsWith('image/')) {
|
||||
|
@ -142,8 +145,8 @@ function getPastedContent(e) {
|
|||
return {text, images};
|
||||
}
|
||||
|
||||
export function initEasyMDEPaste(easyMDE, dropzoneEl) {
|
||||
const editor = new CodeMirrorEditor(easyMDE.codemirror);
|
||||
export function initEasyMDEPaste(easyMDE: EasyMDE, dropzoneEl: HTMLElement) {
|
||||
const editor = new CodeMirrorEditor(easyMDE.codemirror as any);
|
||||
easyMDE.codemirror.on('paste', (_, e) => {
|
||||
const {images} = getPastedContent(e);
|
||||
if (!images.length) return;
|
||||
|
@ -160,28 +163,28 @@ export function initEasyMDEPaste(easyMDE, dropzoneEl) {
|
|||
});
|
||||
}
|
||||
|
||||
export function initTextareaEvents(textarea, dropzoneEl) {
|
||||
export function initTextareaEvents(textarea: HTMLTextAreaElement, dropzoneEl: HTMLElement) {
|
||||
let isShiftDown = false;
|
||||
textarea.addEventListener('keydown', (e) => {
|
||||
textarea.addEventListener('keydown', (e: KeyboardEvent) => {
|
||||
if (e.shiftKey) isShiftDown = true;
|
||||
});
|
||||
textarea.addEventListener('keyup', (e) => {
|
||||
textarea.addEventListener('keyup', (e: KeyboardEvent) => {
|
||||
if (!e.shiftKey) isShiftDown = false;
|
||||
});
|
||||
textarea.addEventListener('paste', (e) => {
|
||||
textarea.addEventListener('paste', (e: ClipboardEvent) => {
|
||||
const {images, text} = getPastedContent(e);
|
||||
if (images.length && dropzoneEl) {
|
||||
handleUploadFiles(new TextareaEditor(textarea), dropzoneEl, images, e);
|
||||
} else if (text) {
|
||||
handleClipboardText(textarea, e, {text, isShiftDown});
|
||||
handleClipboardText(textarea, e, text, isShiftDown);
|
||||
}
|
||||
});
|
||||
textarea.addEventListener('drop', (e) => {
|
||||
textarea.addEventListener('drop', (e: DragEvent) => {
|
||||
if (!e.dataTransfer.files.length) return;
|
||||
if (!dropzoneEl) return;
|
||||
handleUploadFiles(new TextareaEditor(textarea), dropzoneEl, e.dataTransfer.files, e);
|
||||
});
|
||||
dropzoneEl?.dropzone.on(DropzoneCustomEventRemovedFile, ({fileUuid}) => {
|
||||
dropzoneEl?.dropzone.on(DropzoneCustomEventRemovedFile, ({fileUuid}: {fileUuid: string}) => {
|
||||
const newText = removeAttachmentLinksFromMarkdown(textarea.value, fileUuid);
|
||||
if (textarea.value !== newText) textarea.value = newText;
|
||||
});
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import {querySingleVisibleElem} from '../../utils/dom.ts';
|
||||
|
||||
export function handleGlobalEnterQuickSubmit(target) {
|
||||
export function handleGlobalEnterQuickSubmit(target: HTMLElement) {
|
||||
let form = target.closest('form');
|
||||
if (form) {
|
||||
if (!form.checkValidity()) {
|
||||
|
|
|
@ -14,7 +14,7 @@ export function initCompSearchUserBox() {
|
|||
minCharacters: 2,
|
||||
apiSettings: {
|
||||
url: `${appSubUrl}/user/search_candidates?q={query}`,
|
||||
onResponse(response) {
|
||||
onResponse(response: any) {
|
||||
const resultItems = [];
|
||||
const searchQuery = searchUserBox.querySelector('input').value;
|
||||
const searchQueryUppercase = searchQuery.toUpperCase();
|
||||
|
|
|
@ -5,6 +5,7 @@ import {parseIssueHref, parseRepoOwnerPathInfo} from '../../utils.ts';
|
|||
import {createElementFromAttrs, createElementFromHTML} from '../../utils/dom.ts';
|
||||
import {getIssueColor, getIssueIcon} from '../issue.ts';
|
||||
import {debounce} from 'perfect-debounce';
|
||||
import type TextExpanderElement from '@github/text-expander-element';
|
||||
|
||||
const debouncedSuggestIssues = debounce((key: string, text: string) => new Promise<{matched:boolean; fragment?: HTMLElement}>(async (resolve) => {
|
||||
const issuePathInfo = parseIssueHref(window.location.href);
|
||||
|
@ -32,8 +33,8 @@ const debouncedSuggestIssues = debounce((key: string, text: string) => new Promi
|
|||
resolve({matched: true, fragment: ul});
|
||||
}), 100);
|
||||
|
||||
export function initTextExpander(expander) {
|
||||
expander?.addEventListener('text-expander-change', ({detail: {key, provide, text}}) => {
|
||||
export function initTextExpander(expander: TextExpanderElement) {
|
||||
expander?.addEventListener('text-expander-change', ({detail: {key, provide, text}}: Record<string, any>) => {
|
||||
if (key === ':') {
|
||||
const matches = matchEmoji(text);
|
||||
if (!matches.length) return provide({matched: false});
|
||||
|
@ -84,7 +85,7 @@ export function initTextExpander(expander) {
|
|||
provide(debouncedSuggestIssues(key, text));
|
||||
}
|
||||
});
|
||||
expander?.addEventListener('text-expander-value', ({detail}) => {
|
||||
expander?.addEventListener('text-expander-value', ({detail}: Record<string, any>) => {
|
||||
if (detail?.item) {
|
||||
// add a space after @mentions and #issue as it's likely the user wants one
|
||||
const suffix = ['@', '#'].includes(detail.key) ? ' ' : '';
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue