mirror of
https://github.com/go-gitea/gitea
synced 2025-12-07 05:18:29 +00:00
Enable TypeScript strictNullChecks (#35843)
A big step towards enabling strict mode in Typescript. There was definitely a good share of potential bugs while refactoring this. When in doubt, I opted to keep the potentially broken behaviour. Notably, the `DOMEvent` type is gone, it was broken and we're better of with type assertions on `e.target`. --------- Signed-off-by: silverwind <me@silverwind.io> Signed-off-by: wxiaoguang <wxiaoguang@gmail.com> Co-authored-by: delvh <dev.lh@web.de> Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
@@ -81,7 +81,7 @@ export class ComboMarkdownEditor {
|
||||
textareaMarkdownToolbar: HTMLElement;
|
||||
textareaAutosize: any;
|
||||
|
||||
dropzone: HTMLElement;
|
||||
dropzone: HTMLElement | null;
|
||||
attachedDropzoneInst: any;
|
||||
|
||||
previewMode: string;
|
||||
@@ -105,7 +105,7 @@ export class ComboMarkdownEditor {
|
||||
await this.switchToUserPreference();
|
||||
}
|
||||
|
||||
applyEditorHeights(el: HTMLElement, heights: Heights) {
|
||||
applyEditorHeights(el: HTMLElement, heights: Heights | undefined) {
|
||||
if (!heights) return;
|
||||
if (heights.minHeight) el.style.minHeight = heights.minHeight;
|
||||
if (heights.height) el.style.height = heights.height;
|
||||
@@ -114,14 +114,14 @@ export class ComboMarkdownEditor {
|
||||
|
||||
setupContainer() {
|
||||
this.supportEasyMDE = this.container.getAttribute('data-support-easy-mde') === 'true';
|
||||
this.previewMode = this.container.getAttribute('data-content-mode');
|
||||
this.previewUrl = this.container.getAttribute('data-preview-url');
|
||||
this.previewContext = this.container.getAttribute('data-preview-context');
|
||||
initTextExpander(this.container.querySelector('text-expander'));
|
||||
this.previewMode = this.container.getAttribute('data-content-mode')!;
|
||||
this.previewUrl = this.container.getAttribute('data-preview-url')!;
|
||||
this.previewContext = this.container.getAttribute('data-preview-context')!;
|
||||
initTextExpander(this.container.querySelector('text-expander')!);
|
||||
}
|
||||
|
||||
setupTextarea() {
|
||||
this.textarea = this.container.querySelector('.markdown-text-editor');
|
||||
this.textarea = this.container.querySelector('.markdown-text-editor')!;
|
||||
this.textarea._giteaComboMarkdownEditor = this;
|
||||
this.textarea.id = generateElemId(`_combo_markdown_editor_`);
|
||||
this.textarea.addEventListener('input', () => triggerEditorContentChanged(this.container));
|
||||
@@ -131,7 +131,7 @@ export class ComboMarkdownEditor {
|
||||
this.textareaAutosize = autosize(this.textarea, {viewportMarginBottom: 130});
|
||||
}
|
||||
|
||||
this.textareaMarkdownToolbar = this.container.querySelector('markdown-toolbar');
|
||||
this.textareaMarkdownToolbar = this.container.querySelector('markdown-toolbar')!;
|
||||
this.textareaMarkdownToolbar.setAttribute('for', this.textarea.id);
|
||||
for (const el of this.textareaMarkdownToolbar.querySelectorAll('.markdown-toolbar-button')) {
|
||||
// upstream bug: The role code is never executed in base MarkdownButtonElement https://github.com/github/markdown-toolbar-element/issues/70
|
||||
@@ -140,9 +140,9 @@ export class ComboMarkdownEditor {
|
||||
if (el.nodeName === 'BUTTON' && !el.getAttribute('type')) el.setAttribute('type', 'button');
|
||||
}
|
||||
|
||||
const monospaceButton = this.container.querySelector('.markdown-switch-monospace');
|
||||
const monospaceButton = this.container.querySelector('.markdown-switch-monospace')!;
|
||||
const monospaceEnabled = localStorage?.getItem('markdown-editor-monospace') === 'true';
|
||||
const monospaceText = monospaceButton.getAttribute(monospaceEnabled ? 'data-disable-text' : 'data-enable-text');
|
||||
const monospaceText = monospaceButton.getAttribute(monospaceEnabled ? 'data-disable-text' : 'data-enable-text')!;
|
||||
monospaceButton.setAttribute('data-tooltip-content', monospaceText);
|
||||
monospaceButton.setAttribute('aria-checked', String(monospaceEnabled));
|
||||
monospaceButton.addEventListener('click', (e) => {
|
||||
@@ -150,13 +150,13 @@ export class ComboMarkdownEditor {
|
||||
const enabled = localStorage?.getItem('markdown-editor-monospace') !== 'true';
|
||||
localStorage.setItem('markdown-editor-monospace', String(enabled));
|
||||
this.textarea.classList.toggle('tw-font-mono', enabled);
|
||||
const text = monospaceButton.getAttribute(enabled ? 'data-disable-text' : 'data-enable-text');
|
||||
const text = monospaceButton.getAttribute(enabled ? 'data-disable-text' : 'data-enable-text')!;
|
||||
monospaceButton.setAttribute('data-tooltip-content', text);
|
||||
monospaceButton.setAttribute('aria-checked', String(enabled));
|
||||
});
|
||||
|
||||
if (this.supportEasyMDE) {
|
||||
const easymdeButton = this.container.querySelector('.markdown-switch-easymde');
|
||||
const easymdeButton = this.container.querySelector('.markdown-switch-easymde')!;
|
||||
easymdeButton.addEventListener('click', async (e) => {
|
||||
e.preventDefault();
|
||||
this.userPreferredEditor = 'easymde';
|
||||
@@ -173,7 +173,7 @@ export class ComboMarkdownEditor {
|
||||
async setupDropzone() {
|
||||
const dropzoneParentContainer = this.container.getAttribute('data-dropzone-parent-container');
|
||||
if (!dropzoneParentContainer) return;
|
||||
this.dropzone = this.container.closest(this.container.getAttribute('data-dropzone-parent-container'))?.querySelector('.dropzone');
|
||||
this.dropzone = this.container.closest(this.container.getAttribute('data-dropzone-parent-container')!)?.querySelector('.dropzone') ?? null;
|
||||
if (!this.dropzone) return;
|
||||
|
||||
this.attachedDropzoneInst = await initDropzone(this.dropzone);
|
||||
@@ -212,13 +212,14 @@ export class ComboMarkdownEditor {
|
||||
// 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.
|
||||
const tabIdSuffix = generateElemId();
|
||||
this.tabEditor = Array.from(tabs).find((tab) => tab.getAttribute('data-tab-for') === 'markdown-writer');
|
||||
this.tabPreviewer = Array.from(tabs).find((tab) => tab.getAttribute('data-tab-for') === 'markdown-previewer');
|
||||
const tabsArr = Array.from(tabs);
|
||||
this.tabEditor = tabsArr.find((tab) => tab.getAttribute('data-tab-for') === 'markdown-writer')!;
|
||||
this.tabPreviewer = tabsArr.find((tab) => tab.getAttribute('data-tab-for') === 'markdown-previewer')!;
|
||||
this.tabEditor.setAttribute('data-tab', `markdown-writer-${tabIdSuffix}`);
|
||||
this.tabPreviewer.setAttribute('data-tab', `markdown-previewer-${tabIdSuffix}`);
|
||||
|
||||
const panelEditor = this.container.querySelector('.ui.tab[data-tab-panel="markdown-writer"]');
|
||||
const panelPreviewer = this.container.querySelector('.ui.tab[data-tab-panel="markdown-previewer"]');
|
||||
const panelEditor = this.container.querySelector('.ui.tab[data-tab-panel="markdown-writer"]')!;
|
||||
const panelPreviewer = this.container.querySelector('.ui.tab[data-tab-panel="markdown-previewer"]')!;
|
||||
panelEditor.setAttribute('data-tab', `markdown-writer-${tabIdSuffix}`);
|
||||
panelPreviewer.setAttribute('data-tab', `markdown-previewer-${tabIdSuffix}`);
|
||||
|
||||
@@ -254,8 +255,8 @@ export class ComboMarkdownEditor {
|
||||
}
|
||||
|
||||
initMarkdownButtonTableAdd() {
|
||||
const addTableButton = this.container.querySelector('.markdown-button-table-add');
|
||||
const addTablePanel = this.container.querySelector('.markdown-add-table-panel');
|
||||
const addTableButton = this.container.querySelector('.markdown-button-table-add')!;
|
||||
const addTablePanel = this.container.querySelector('.markdown-add-table-panel')!;
|
||||
// here the tippy can't attach to the button because the button already owns a tippy for tooltip
|
||||
const addTablePanelTippy = createTippy(addTablePanel, {
|
||||
content: addTablePanel,
|
||||
@@ -267,9 +268,9 @@ export class ComboMarkdownEditor {
|
||||
});
|
||||
addTableButton.addEventListener('click', () => addTablePanelTippy.show());
|
||||
|
||||
addTablePanel.querySelector('.ui.button.primary').addEventListener('click', () => {
|
||||
let rows = parseInt(addTablePanel.querySelector<HTMLInputElement>('[name=rows]').value);
|
||||
let cols = parseInt(addTablePanel.querySelector<HTMLInputElement>('[name=cols]').value);
|
||||
addTablePanel.querySelector('.ui.button.primary')!.addEventListener('click', () => {
|
||||
let rows = parseInt(addTablePanel.querySelector<HTMLInputElement>('[name=rows]')!.value);
|
||||
let cols = parseInt(addTablePanel.querySelector<HTMLInputElement>('[name=cols]')!.value);
|
||||
rows = Math.max(1, Math.min(100, rows));
|
||||
cols = Math.max(1, Math.min(100, cols));
|
||||
textareaInsertText(this.textarea, `\n${this.generateMarkdownTable(rows, cols)}\n\n`);
|
||||
@@ -360,7 +361,7 @@ export class ComboMarkdownEditor {
|
||||
}
|
||||
},
|
||||
});
|
||||
this.applyEditorHeights(this.container.querySelector('.CodeMirror-scroll'), this.options.editorHeights);
|
||||
this.applyEditorHeights(this.container.querySelector('.CodeMirror-scroll')!, this.options.editorHeights);
|
||||
await attachTribute(this.easyMDE.codemirror.getInputField());
|
||||
if (this.dropzone) {
|
||||
initEasyMDEPaste(this.easyMDE, this.dropzone);
|
||||
@@ -401,10 +402,10 @@ export class ComboMarkdownEditor {
|
||||
}
|
||||
}
|
||||
|
||||
get userPreferredEditor() {
|
||||
return window.localStorage.getItem(`markdown-editor-${this.previewMode ?? 'default'}`);
|
||||
get userPreferredEditor(): string {
|
||||
return window.localStorage.getItem(`markdown-editor-${this.previewMode ?? 'default'}`) || '';
|
||||
}
|
||||
set userPreferredEditor(s) {
|
||||
set userPreferredEditor(s: string) {
|
||||
window.localStorage.setItem(`markdown-editor-${this.previewMode ?? 'default'}`, s);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {showElem, type DOMEvent} from '../../utils/dom.ts';
|
||||
import {showElem} from '../../utils/dom.ts';
|
||||
|
||||
type CropperOpts = {
|
||||
container: HTMLElement,
|
||||
@@ -17,6 +17,7 @@ async function initCompCropper({container, fileInput, imageSource}: CropperOpts)
|
||||
crop() {
|
||||
const canvas = cropper.getCroppedCanvas();
|
||||
canvas.toBlob((blob) => {
|
||||
if (!blob) return;
|
||||
const croppedFileName = currentFileName.replace(/\.[^.]{3,4}$/, '.png');
|
||||
const croppedFile = new File([blob], croppedFileName, {type: 'image/png', lastModified: currentFileLastModified});
|
||||
const dataTransfer = new DataTransfer();
|
||||
@@ -26,9 +27,9 @@ async function initCompCropper({container, fileInput, imageSource}: CropperOpts)
|
||||
},
|
||||
});
|
||||
|
||||
fileInput.addEventListener('input', (e: DOMEvent<Event, HTMLInputElement>) => {
|
||||
const files = e.target.files;
|
||||
if (files?.length > 0) {
|
||||
fileInput.addEventListener('input', (e) => {
|
||||
const files = (e.target as HTMLInputElement).files;
|
||||
if (files?.length) {
|
||||
currentFileName = files[0].name;
|
||||
currentFileLastModified = files[0].lastModified;
|
||||
const fileURL = URL.createObjectURL(files[0]);
|
||||
@@ -42,6 +43,6 @@ async function initCompCropper({container, fileInput, imageSource}: CropperOpts)
|
||||
export async function initAvatarUploaderWithCropper(fileInput: HTMLInputElement) {
|
||||
const panel = fileInput.nextElementSibling as HTMLElement;
|
||||
if (!panel?.matches('.cropper-panel')) throw new Error('Missing cropper panel for avatar uploader');
|
||||
const imageSource = panel.querySelector<HTMLImageElement>('.cropper-source');
|
||||
const imageSource = panel.querySelector<HTMLImageElement>('.cropper-source')!;
|
||||
await initCompCropper({container: panel, fileInput, imageSource});
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ test('textareaSplitLines', () => {
|
||||
});
|
||||
|
||||
test('markdownHandleIndention', () => {
|
||||
const testInput = (input: string, expected?: string) => {
|
||||
const testInput = (input: string, expected: string | null) => {
|
||||
const inputPos = input.indexOf('|');
|
||||
input = input.replaceAll('|', '');
|
||||
const ret = markdownHandleIndention({value: input, selStart: inputPos, selEnd: inputPos});
|
||||
|
||||
@@ -45,7 +45,8 @@ function handleIndentSelection(textarea: HTMLTextAreaElement, e: KeyboardEvent)
|
||||
}
|
||||
|
||||
// re-calculating the selection range
|
||||
let newSelStart, newSelEnd;
|
||||
let newSelStart: number | null = null;
|
||||
let newSelEnd: number | null = null;
|
||||
pos = 0;
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
if (i === selectedLines[0]) {
|
||||
@@ -134,7 +135,7 @@ export function markdownHandleIndention(tvs: TextareaValueSelection): MarkdownHa
|
||||
|
||||
// parse the indention
|
||||
let lineContent = line;
|
||||
const indention = /^\s*/.exec(lineContent)[0];
|
||||
const indention = (/^\s*/.exec(lineContent) || [''])[0];
|
||||
lineContent = lineContent.slice(indention.length);
|
||||
if (linesBuf.inlinePos <= indention.length) return unhandled; // if cursor is at the indention, do nothing, let the browser handle it
|
||||
|
||||
@@ -177,7 +178,7 @@ export function markdownHandleIndention(tvs: TextareaValueSelection): MarkdownHa
|
||||
|
||||
function handleNewline(textarea: HTMLTextAreaElement, e: Event) {
|
||||
const ret = markdownHandleIndention({value: textarea.value, selStart: textarea.selectionStart, selEnd: textarea.selectionEnd});
|
||||
if (!ret.handled) return;
|
||||
if (!ret.handled || !ret.valueSelection) return; // FIXME: the "handled" seems redundant, only valueSelection is enough (null for unhandled)
|
||||
e.preventDefault();
|
||||
textarea.value = ret.valueSelection.value;
|
||||
textarea.setSelectionRange(ret.valueSelection.selStart, ret.valueSelection.selEnd);
|
||||
|
||||
@@ -121,7 +121,10 @@ function getPastedImages(e: ClipboardEvent) {
|
||||
const images: Array<File> = [];
|
||||
for (const item of e.clipboardData?.items ?? []) {
|
||||
if (item.type?.startsWith('image/')) {
|
||||
images.push(item.getAsFile());
|
||||
const file = item.getAsFile();
|
||||
if (file) {
|
||||
images.push(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
return images;
|
||||
@@ -135,7 +138,7 @@ export function initEasyMDEPaste(easyMDE: EasyMDE, dropzoneEl: HTMLElement) {
|
||||
handleUploadFiles(editor, dropzoneEl, images, e);
|
||||
});
|
||||
easyMDE.codemirror.on('drop', (_, e) => {
|
||||
if (!e.dataTransfer.files.length) return;
|
||||
if (!e.dataTransfer?.files.length) return;
|
||||
handleUploadFiles(editor, dropzoneEl, e.dataTransfer.files, e);
|
||||
});
|
||||
dropzoneEl.dropzone.on(DropzoneCustomEventRemovedFile, ({fileUuid}) => {
|
||||
@@ -145,7 +148,7 @@ export function initEasyMDEPaste(easyMDE: EasyMDE, dropzoneEl: HTMLElement) {
|
||||
});
|
||||
}
|
||||
|
||||
export function initTextareaEvents(textarea: HTMLTextAreaElement, dropzoneEl: HTMLElement) {
|
||||
export function initTextareaEvents(textarea: HTMLTextAreaElement, dropzoneEl: HTMLElement | null) {
|
||||
subscribe(textarea); // enable paste features
|
||||
textarea.addEventListener('paste', (e: ClipboardEvent) => {
|
||||
const images = getPastedImages(e);
|
||||
@@ -154,7 +157,7 @@ export function initTextareaEvents(textarea: HTMLTextAreaElement, dropzoneEl: HT
|
||||
}
|
||||
});
|
||||
textarea.addEventListener('drop', (e: DragEvent) => {
|
||||
if (!e.dataTransfer.files.length) return;
|
||||
if (!e.dataTransfer?.files.length) return;
|
||||
if (!dropzoneEl) return;
|
||||
handleUploadFiles(new TextareaEditor(textarea), dropzoneEl, e.dataTransfer.files, e);
|
||||
});
|
||||
|
||||
@@ -14,17 +14,17 @@ export function initCompLabelEdit(pageSelector: string) {
|
||||
const elModal = pageContent.querySelector<HTMLElement>('#issue-label-edit-modal');
|
||||
if (!elModal) return;
|
||||
|
||||
const elLabelId = elModal.querySelector<HTMLInputElement>('input[name="id"]');
|
||||
const elNameInput = elModal.querySelector<HTMLInputElement>('.label-name-input');
|
||||
const elExclusiveField = elModal.querySelector('.label-exclusive-input-field');
|
||||
const elExclusiveInput = elModal.querySelector<HTMLInputElement>('.label-exclusive-input');
|
||||
const elExclusiveWarning = elModal.querySelector('.label-exclusive-warning');
|
||||
const elExclusiveOrderField = elModal.querySelector<HTMLInputElement>('.label-exclusive-order-input-field');
|
||||
const elExclusiveOrderInput = elModal.querySelector<HTMLInputElement>('.label-exclusive-order-input');
|
||||
const elIsArchivedField = elModal.querySelector('.label-is-archived-input-field');
|
||||
const elIsArchivedInput = elModal.querySelector<HTMLInputElement>('.label-is-archived-input');
|
||||
const elDescInput = elModal.querySelector<HTMLInputElement>('.label-desc-input');
|
||||
const elColorInput = elModal.querySelector<HTMLInputElement>('.color-picker-combo input');
|
||||
const elLabelId = elModal.querySelector<HTMLInputElement>('input[name="id"]')!;
|
||||
const elNameInput = elModal.querySelector<HTMLInputElement>('.label-name-input')!;
|
||||
const elExclusiveField = elModal.querySelector('.label-exclusive-input-field')!;
|
||||
const elExclusiveInput = elModal.querySelector<HTMLInputElement>('.label-exclusive-input')!;
|
||||
const elExclusiveWarning = elModal.querySelector('.label-exclusive-warning')!;
|
||||
const elExclusiveOrderField = elModal.querySelector<HTMLInputElement>('.label-exclusive-order-input-field')!;
|
||||
const elExclusiveOrderInput = elModal.querySelector<HTMLInputElement>('.label-exclusive-order-input')!;
|
||||
const elIsArchivedField = elModal.querySelector('.label-is-archived-input-field')!;
|
||||
const elIsArchivedInput = elModal.querySelector<HTMLInputElement>('.label-is-archived-input')!;
|
||||
const elDescInput = elModal.querySelector<HTMLInputElement>('.label-desc-input')!;
|
||||
const elColorInput = elModal.querySelector<HTMLInputElement>('.color-picker-combo input')!;
|
||||
|
||||
const syncModalUi = () => {
|
||||
const hasScope = nameHasScope(elNameInput.value);
|
||||
@@ -37,13 +37,13 @@ export function initCompLabelEdit(pageSelector: string) {
|
||||
if (parseInt(elExclusiveOrderInput.value) <= 0) {
|
||||
elExclusiveOrderInput.style.color = 'var(--color-placeholder-text) !important';
|
||||
} else {
|
||||
elExclusiveOrderInput.style.color = null;
|
||||
elExclusiveOrderInput.style.removeProperty('color');
|
||||
}
|
||||
};
|
||||
|
||||
const showLabelEditModal = (btn:HTMLElement) => {
|
||||
// the "btn" should contain the label's attributes by its `data-label-xxx` attributes
|
||||
const form = elModal.querySelector<HTMLFormElement>('form');
|
||||
const form = elModal.querySelector<HTMLFormElement>('form')!;
|
||||
elLabelId.value = btn.getAttribute('data-label-id') || '';
|
||||
elNameInput.value = btn.getAttribute('data-label-name') || '';
|
||||
elExclusiveOrderInput.value = btn.getAttribute('data-label-exclusive-order') || '0';
|
||||
@@ -59,7 +59,7 @@ export function initCompLabelEdit(pageSelector: string) {
|
||||
// if a label was not exclusive but has issues, then it should warn user if it will become exclusive
|
||||
const numIssues = parseInt(btn.getAttribute('data-label-num-issues') || '0');
|
||||
elModal.toggleAttribute('data-need-warn-exclusive', !elExclusiveInput.checked && numIssues > 0);
|
||||
elModal.querySelector('.header').textContent = isEdit ? elModal.getAttribute('data-text-edit-label') : elModal.getAttribute('data-text-new-label');
|
||||
elModal.querySelector('.header')!.textContent = isEdit ? elModal.getAttribute('data-text-edit-label') : elModal.getAttribute('data-text-new-label');
|
||||
|
||||
const curPageLink = elModal.getAttribute('data-current-page-link');
|
||||
form.action = isEdit ? `${curPageLink}/edit` : `${curPageLink}/new`;
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
import {POST} from '../../modules/fetch.ts';
|
||||
import type {DOMEvent} from '../../utils/dom.ts';
|
||||
import {registerGlobalEventFunc} from '../../modules/observer.ts';
|
||||
|
||||
export function initCompReactionSelector() {
|
||||
registerGlobalEventFunc('click', 'onCommentReactionButtonClick', async (target: HTMLElement, e: DOMEvent<MouseEvent>) => {
|
||||
registerGlobalEventFunc('click', 'onCommentReactionButtonClick', async (target: HTMLElement, e: Event) => {
|
||||
// there are 2 places for the "reaction" buttons, one is the top-right reaction menu, one is the bottom of the comment
|
||||
e.preventDefault();
|
||||
|
||||
if (target.classList.contains('disabled')) return;
|
||||
|
||||
const actionUrl = target.closest('[data-action-url]').getAttribute('data-action-url');
|
||||
const reactionContent = target.getAttribute('data-reaction-content');
|
||||
const actionUrl = target.closest('[data-action-url]')!.getAttribute('data-action-url');
|
||||
const reactionContent = target.getAttribute('data-reaction-content')!;
|
||||
|
||||
const commentContainer = target.closest('.comment-container');
|
||||
const commentContainer = target.closest('.comment-container')!;
|
||||
|
||||
const bottomReactions = commentContainer.querySelector('.bottom-reactions'); // may not exist if there is no reaction
|
||||
const bottomReactionBtn = bottomReactions?.querySelector(`a[data-reaction-content="${CSS.escape(reactionContent)}"]`);
|
||||
|
||||
@@ -17,7 +17,7 @@ export function initCompSearchUserBox() {
|
||||
url: `${appSubUrl}/user/search_candidates?q={query}&orgs=${includeOrgs}`,
|
||||
onResponse(response: any) {
|
||||
const resultItems = [];
|
||||
const searchQuery = searchUserBox.querySelector('input').value;
|
||||
const searchQuery = searchUserBox.querySelector('input')!.value;
|
||||
const searchQueryUppercase = searchQuery.toUpperCase();
|
||||
for (const item of response.data) {
|
||||
const resultItem = {
|
||||
|
||||
@@ -37,7 +37,7 @@ async function fetchIssueSuggestions(key: string, text: string): Promise<TextExp
|
||||
export function initTextExpander(expander: TextExpanderElement) {
|
||||
if (!expander) return;
|
||||
|
||||
const textarea = expander.querySelector<HTMLTextAreaElement>('textarea');
|
||||
const textarea = expander.querySelector<HTMLTextAreaElement>('textarea')!;
|
||||
|
||||
// help to fix the text-expander "multiword+promise" bug: do not show the popup when there is no "#" before current line
|
||||
const shouldShowIssueSuggestions = () => {
|
||||
@@ -64,6 +64,7 @@ export function initTextExpander(expander: TextExpanderElement) {
|
||||
}, 300); // to match onInputDebounce delay
|
||||
|
||||
expander.addEventListener('text-expander-change', (e: TextExpanderChangeEvent) => {
|
||||
if (!e.detail) return;
|
||||
const {key, text, provide} = e.detail;
|
||||
if (key === ':') {
|
||||
const matches = matchEmoji(text);
|
||||
|
||||
@@ -27,7 +27,7 @@ export function initCompWebHookEditor() {
|
||||
if (httpMethodInput) {
|
||||
const updateContentType = function () {
|
||||
const visible = httpMethodInput.value === 'POST';
|
||||
toggleElem(document.querySelector('#content_type').closest('.field'), visible);
|
||||
toggleElem(document.querySelector('#content_type')!.closest('.field')!, visible);
|
||||
};
|
||||
updateContentType();
|
||||
httpMethodInput.addEventListener('change', updateContentType);
|
||||
@@ -36,9 +36,12 @@ export function initCompWebHookEditor() {
|
||||
// Test delivery
|
||||
document.querySelector<HTMLButtonElement>('#test-delivery')?.addEventListener('click', async function () {
|
||||
this.classList.add('is-loading', 'disabled');
|
||||
await POST(this.getAttribute('data-link'));
|
||||
await POST(this.getAttribute('data-link')!);
|
||||
setTimeout(() => {
|
||||
window.location.href = this.getAttribute('data-redirect');
|
||||
const redirectUrl = this.getAttribute('data-redirect');
|
||||
if (redirectUrl) {
|
||||
window.location.href = redirectUrl;
|
||||
}
|
||||
}, 5000);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user