1
0
Fork 0
mirror of https://codeberg.org/forgejo/forgejo.git synced 2025-07-27 05:29:41 +02:00

fix(ui): multiple ComboMarkdownEditors on one page interfere (#8417)

When there are multiple combo-markdown-editors, then only the first will get changes from the toolbar buttons.
Fixes: #6742

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8417
Reviewed-by: Beowulf <beowulf@beocode.eu>
Co-authored-by: zokki <zokki.softwareschmiede@gmail.com>
Co-committed-by: zokki <zokki.softwareschmiede@gmail.com>
This commit is contained in:
zokki 2025-07-14 13:24:45 +02:00 committed by Beowulf
parent 81e59014da
commit 1937fcf476
3 changed files with 91 additions and 17 deletions

View file

@ -11,8 +11,6 @@ import {initTextExpander} from './TextExpander.js';
import {showErrorToast, showHintToast} from '../../modules/toast.js';
import {POST} from '../../modules/fetch.js';
let elementIdCounter = 0;
/**
* validate if the given textarea is non-empty.
* @param {HTMLElement} textarea - The textarea element to be validated.
@ -39,10 +37,13 @@ export function validateTextareaNonEmpty(textarea) {
const listPrefixRegex = /^\s*((\d+)[.)]\s|[-*+]\s{1,4}\[[ x]\]\s?|[-*+]\s|(>\s?)+)?/;
class ComboMarkdownEditor {
static idSuffixCounter = 0;
constructor(container, options = {}) {
container._giteaComboMarkdownEditor = this;
this.options = options;
this.container = container;
this.elementIdSuffix = ComboMarkdownEditor.idSuffixCounter++;
}
async init() {
@ -55,8 +56,6 @@ class ComboMarkdownEditor {
this.setupLinkInserter();
await this.switchToUserPreference();
elementIdCounter++;
}
applyEditorHeights(el, heights) {
@ -74,7 +73,7 @@ class ComboMarkdownEditor {
setupTextarea() {
this.textarea = this.container.querySelector('.markdown-text-editor');
this.textarea._giteaComboMarkdownEditor = this;
this.textarea.id = `_combo_markdown_editor_${elementIdCounter}`;
this.textarea.id = `_combo_markdown_editor_${this.elementIdSuffix}`;
this.textarea.addEventListener('input', (e) => this.options?.onContentChanged?.(this, e));
this.applyEditorHeights(this.textarea, this.options.editorHeights);
@ -96,8 +95,8 @@ class ComboMarkdownEditor {
this.textareaMarkdownToolbar.querySelector('button[data-md-action="unindent"]')?.addEventListener('click', () => {
this.indentSelection(true, false);
});
this.textareaMarkdownToolbar.querySelector('button[data-md-action="new-table"]')?.setAttribute('data-modal', `div[data-markdown-table-modal-id="${elementIdCounter}"]`);
this.textareaMarkdownToolbar.querySelector('button[data-md-action="new-link"]')?.setAttribute('data-modal', `div[data-markdown-link-modal-id="${elementIdCounter}"]`);
this.textareaMarkdownToolbar.querySelector('button[data-md-action="new-table"]')?.setAttribute('data-modal', `div[data-markdown-table-modal-id="${this.elementIdSuffix}"]`);
this.textareaMarkdownToolbar.querySelector('button[data-md-action="new-link"]')?.setAttribute('data-modal', `div[data-markdown-link-modal-id="${this.elementIdSuffix}"]`);
// Track whether any actual input or pointer action was made after focusing, and only intercept Tab presses after that.
this.tabEnabled = false;
@ -195,7 +194,7 @@ class ComboMarkdownEditor {
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');
this.dropzone = this.container.closest(dropzoneParentContainer)?.querySelector('.dropzone');
}
}
@ -207,13 +206,13 @@ class ComboMarkdownEditor {
// So here it uses our defined "data-tab-for" and "data-tab-panel" to generate the "data-tab" attribute for Fomantic.
const tabEditor = Array.from(tabs).find((tab) => tab.getAttribute('data-tab-for') === 'markdown-writer');
const tabPreviewer = Array.from(tabs).find((tab) => tab.getAttribute('data-tab-for') === 'markdown-previewer');
tabEditor.setAttribute('data-tab', `markdown-writer-${elementIdCounter}`);
tabPreviewer.setAttribute('data-tab', `markdown-previewer-${elementIdCounter}`);
tabEditor.setAttribute('data-tab', `markdown-writer-${this.elementIdSuffix}`);
tabPreviewer.setAttribute('data-tab', `markdown-previewer-${this.elementIdSuffix}`);
const toolbar = $container[0].querySelector('markdown-toolbar');
const panelEditor = $container[0].querySelector('.ui.tab[data-tab-panel="markdown-writer"]');
const panelPreviewer = $container[0].querySelector('.ui.tab[data-tab-panel="markdown-previewer"]');
panelEditor.setAttribute('data-tab', `markdown-writer-${elementIdCounter}`);
panelPreviewer.setAttribute('data-tab', `markdown-previewer-${elementIdCounter}`);
panelEditor.setAttribute('data-tab', `markdown-writer-${this.elementIdSuffix}`);
panelPreviewer.setAttribute('data-tab', `markdown-previewer-${this.elementIdSuffix}`);
tabEditor.addEventListener('click', () => {
toolbar.classList.remove('markdown-toolbar-hidden');
@ -276,10 +275,10 @@ class ComboMarkdownEditor {
setupTableInserter() {
const newTableModal = this.container.querySelector('div[data-modal-name="new-markdown-table"]');
newTableModal.setAttribute('data-markdown-table-modal-id', elementIdCounter);
newTableModal.setAttribute('data-markdown-table-modal-id', this.elementIdSuffix);
const button = newTableModal.querySelector('button[data-selector-name="ok-button"]');
button.setAttribute('data-element-id', elementIdCounter);
button.setAttribute('data-element-id', this.elementIdSuffix);
button.addEventListener('click', this.addNewTable);
}
@ -311,8 +310,8 @@ class ComboMarkdownEditor {
setupLinkInserter() {
const newLinkModal = this.container.querySelector('div[data-modal-name="new-markdown-link"]');
newLinkModal.setAttribute('data-markdown-link-modal-id', elementIdCounter);
const textarea = document.getElementById(`_combo_markdown_editor_${elementIdCounter}`);
newLinkModal.setAttribute('data-markdown-link-modal-id', this.elementIdSuffix);
const textarea = document.getElementById(`_combo_markdown_editor_${this.elementIdSuffix}`);
$(newLinkModal).modal({
// Pre-fill the description field from the selection to create behavior similar
@ -331,7 +330,7 @@ class ComboMarkdownEditor {
});
const button = newLinkModal.querySelector('button[data-selector-name="ok-button"]');
button.setAttribute('data-element-id', elementIdCounter);
button.setAttribute('data-element-id', this.elementIdSuffix);
button.addEventListener('click', this.addNewLink);
}