1
1
mirror of https://github.com/go-gitea/gitea synced 2024-06-27 13:45:51 +00:00

Remove jQuery from repo wiki creation page (#29271)

- Switched to plain JavaScript
- Tested the wiki creation form functionality and it works as before

# Demo using JavaScript without jQuery

![action](https://github.com/go-gitea/gitea/assets/20454870/2dfc95fd-40cc-4ffb-9ae6-50f798fddd67)

---------

Signed-off-by: Yarden Shoham <git@yardenshoham.com>
Co-authored-by: silverwind <me@silverwind.io>
This commit is contained in:
Yarden Shoham 2024-02-20 12:37:37 +02:00 committed by GitHub
parent 8c21bc0d51
commit ade1110e8b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 51 additions and 37 deletions

View File

@ -2,7 +2,7 @@ import '@github/markdown-toolbar-element';
import '@github/text-expander-element'; import '@github/text-expander-element';
import $ from 'jquery'; import $ from 'jquery';
import {attachTribute} from '../tribute.js'; import {attachTribute} from '../tribute.js';
import {hideElem, showElem, autosize} from '../../utils/dom.js'; import {hideElem, showElem, autosize, isElemVisible} from '../../utils/dom.js';
import {initEasyMDEImagePaste, initTextareaImagePaste} from './ImagePaste.js'; import {initEasyMDEImagePaste, initTextareaImagePaste} from './ImagePaste.js';
import {handleGlobalEnterQuickSubmit} from './QuickSubmit.js'; import {handleGlobalEnterQuickSubmit} from './QuickSubmit.js';
import {renderPreviewPanelContent} from '../repo-editor.js'; import {renderPreviewPanelContent} from '../repo-editor.js';
@ -14,17 +14,17 @@ let elementIdCounter = 0;
/** /**
* validate if the given textarea is non-empty. * validate if the given textarea is non-empty.
* @param {jQuery} $textarea * @param {HTMLElement} textarea - The textarea element to be validated.
* @returns {boolean} returns true if validation succeeded. * @returns {boolean} returns true if validation succeeded.
*/ */
export function validateTextareaNonEmpty($textarea) { export function validateTextareaNonEmpty(textarea) {
// When using EasyMDE, the original edit area HTML element is hidden, breaking HTML5 input validation. // 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. // The workaround (https://github.com/sparksuite/simplemde-markdown-editor/issues/324) doesn't work with contenteditable, so we just show an alert.
if (!$textarea.val()) { if (!textarea.value) {
if ($textarea.is(':visible')) { if (isElemVisible(textarea)) {
$textarea.prop('required', true); textarea.required = true;
const $form = $textarea.parents('form'); const form = textarea.closest('form');
$form[0]?.reportValidity(); form?.reportValidity();
} else { } else {
// The alert won't hurt users too much, because we are dropping the EasyMDE and the check only occurs in a few places. // The alert won't hurt users too much, because we are dropping the EasyMDE and the check only occurs in a few places.
showErrorToast('Require non-empty content'); showErrorToast('Require non-empty content');

View File

@ -47,8 +47,8 @@ function initRepoDiffConversationForm() {
e.preventDefault(); e.preventDefault();
const $form = $(e.target); const $form = $(e.target);
const $textArea = $form.find('textarea'); const textArea = e.target.querySelector('textarea');
if (!validateTextareaNonEmpty($textArea)) { if (!validateTextareaNonEmpty(textArea)) {
return; return;
} }

View File

@ -1,50 +1,51 @@
import $ from 'jquery';
import {initMarkupContent} from '../markup/content.js'; import {initMarkupContent} from '../markup/content.js';
import {validateTextareaNonEmpty, initComboMarkdownEditor} from './comp/ComboMarkdownEditor.js'; import {validateTextareaNonEmpty, initComboMarkdownEditor} from './comp/ComboMarkdownEditor.js';
import {fomanticMobileScreen} from '../modules/fomantic.js'; import {fomanticMobileScreen} from '../modules/fomantic.js';
import {POST} from '../modules/fetch.js';
const {csrfToken} = window.config;
async function initRepoWikiFormEditor() { async function initRepoWikiFormEditor() {
const $editArea = $('.repository.wiki .combo-markdown-editor textarea'); const editArea = document.querySelector('.repository.wiki .combo-markdown-editor textarea');
if (!$editArea.length) return; if (!editArea) return;
const $form = $('.repository.wiki.new .ui.form'); const form = document.querySelector('.repository.wiki.new .ui.form');
const $editorContainer = $form.find('.combo-markdown-editor'); const editorContainer = form.querySelector('.combo-markdown-editor');
let editor; let editor;
let renderRequesting = false; let renderRequesting = false;
let lastContent; let lastContent;
const renderEasyMDEPreview = function () { const renderEasyMDEPreview = async function () {
if (renderRequesting) return; if (renderRequesting) return;
const $previewFull = $editorContainer.find('.EasyMDEContainer .editor-preview-active'); const previewFull = editorContainer.querySelector('.EasyMDEContainer .editor-preview-active');
const $previewSide = $editorContainer.find('.EasyMDEContainer .editor-preview-active-side'); const previewSide = editorContainer.querySelector('.EasyMDEContainer .editor-preview-active-side');
const $previewTarget = $previewSide.length ? $previewSide : $previewFull; const previewTarget = previewSide || previewFull;
const newContent = $editArea.val(); const newContent = editArea.value;
if (editor && $previewTarget.length && lastContent !== newContent) { if (editor && previewTarget && lastContent !== newContent) {
renderRequesting = true; renderRequesting = true;
$.post(editor.previewUrl, { const formData = new FormData();
_csrf: csrfToken, formData.append('mode', editor.previewMode);
mode: editor.previewMode, formData.append('context', editor.previewContext);
context: editor.previewContext, formData.append('text', newContent);
text: newContent, formData.append('wiki', editor.previewWiki);
wiki: editor.previewWiki, try {
}).done((data) => { const response = await POST(editor.previewUrl, {data: formData});
const data = await response.text();
lastContent = newContent; lastContent = newContent;
$previewTarget.html(`<div class="markup ui segment">${data}</div>`); previewTarget.innerHTML = `<div class="markup ui segment">${data}</div>`;
initMarkupContent(); initMarkupContent();
}).always(() => { } catch (error) {
console.error('Error rendering preview:', error);
} finally {
renderRequesting = false; renderRequesting = false;
setTimeout(renderEasyMDEPreview, 1000); setTimeout(renderEasyMDEPreview, 1000);
}); }
} else { } else {
setTimeout(renderEasyMDEPreview, 1000); setTimeout(renderEasyMDEPreview, 1000);
} }
}; };
renderEasyMDEPreview(); renderEasyMDEPreview();
editor = await initComboMarkdownEditor($editorContainer, { editor = await initComboMarkdownEditor(editorContainer, {
useScene: 'wiki', useScene: 'wiki',
// EasyMDE has some problems of height definition, it has inline style height 300px by default, so we also use inline styles to override it. // EasyMDE has some problems of height definition, it has inline style height 300px by default, so we also use inline styles to override it.
// And another benefit is that we only need to write the style once for both editors. // And another benefit is that we only need to write the style once for both editors.
@ -64,9 +65,10 @@ async function initRepoWikiFormEditor() {
}, },
}); });
$form.on('submit', () => { form.addEventListener('submit', (e) => {
if (!validateTextareaNonEmpty($editArea)) { if (!validateTextareaNonEmpty(editArea)) {
return false; e.preventDefault();
e.stopPropagation();
} }
}); });
} }

View File

@ -227,3 +227,15 @@ export function initSubmitEventPolyfill() {
document.body.addEventListener('click', submitEventPolyfillListener); document.body.addEventListener('click', submitEventPolyfillListener);
document.body.addEventListener('focus', submitEventPolyfillListener); document.body.addEventListener('focus', submitEventPolyfillListener);
} }
/**
* Check if an element is visible, equivalent to jQuery's `:visible` pseudo.
* Note: This function doesn't account for all possible visibility scenarios.
* @param {HTMLElement} element The element to check.
* @returns {boolean} True if the element is visible.
*/
export function isElemVisible(element) {
if (!element) return false;
return Boolean(element.offsetWidth || element.offsetHeight || element.getClientRects().length);
}