Replace events editor jQuery UI's dialog with a modal

Signed-off-by: Maurício Meneghini Fauth <mauricio@mfauth.net>
This commit is contained in:
Maurício Meneghini Fauth 2025-05-14 12:42:53 -03:00
parent 475780adb0
commit dd0c8a4c88
No known key found for this signature in database
GPG Key ID: 6A16FD38AFC89CC8
3 changed files with 121 additions and 93 deletions

View File

@ -15,11 +15,6 @@ AJAX.registerTeardown('database/events.js', function () {
});
const DatabaseEvents = {
/**
* @var $ajaxDialog Query object containing the reference to the
* dialog that contains the editor
*/
$ajaxDialog: null,
/**
* @var syntaxHiglighter Reference to the codemirror editor
*/
@ -176,24 +171,10 @@ const DatabaseEvents = {
// We have successfully fetched the editor form
ajaxRemoveMessage($msg);
/**
* @var buttonOptions Object containing options
* for jQueryUI dialog buttons
*/
var buttonOptions = {
[window.Messages.strGo]: {
text: window.Messages.strGo,
class: 'btn btn-primary',
},
[window.Messages.strClose]: {
text: window.Messages.strClose,
class: 'btn btn-secondary',
},
};
// Now define the function that is called when
// the user presses the "Go" button
// @ts-ignore
buttonOptions[window.Messages.strGo].click = function () {
let isEditMode = false;
function eventsEditorModalSaveEventHandler () {
// Move the data from the codemirror editor back to the
// textarea, where it can be used in the form submission.
if (typeof window.CodeMirror !== 'undefined') {
@ -221,10 +202,10 @@ const DatabaseEvents = {
// Item created successfully
ajaxRemoveMessage($msg);
slidingMessage(data.message);
that.$ajaxDialog.dialog('close');
window.bootstrap.Modal.getOrCreateInstance('#eventsEditorModal').hide();
// If we are in 'edit' mode, we must
// remove the reference to the old row.
if (mode === 'edit' && $editRow !== null) {
if (isEditMode && $editRow !== null) {
$editRow.remove();
}
@ -317,75 +298,61 @@ const DatabaseEvents = {
}
Navigation.reload();
}); // end $.post()
} // end "if (that.validate())"
}; // end of function that handles the submission of the Editor
// @ts-ignore
buttonOptions[window.Messages.strClose].click = function () {
$(this).dialog('close');
};
/**
* Display the dialog to the user
*/
that.$ajaxDialog = $('<div id="rteDialog">' + data.message + '</div>').dialog({
classes: {
'ui-dialog-titlebar-close': 'btn-close'
},
height: 500,
width: '70%',
minWidth: 500,
// @ts-ignore
buttons: buttonOptions,
// Issue #15810 - use button titles for modals (eg: new procedure)
// Respect the order: title on href tag, href content, title sent in response
title: $this.attr('title') || $this.text() || $(data.title).text(),
modal: true,
open: function () {
$('#rteDialog').dialog('option', 'max-height', $(window).height());
if ($('#rteDialog').parents('.ui-dialog').height() > $(window).height()) {
$('#rteDialog').dialog('option', 'height', $(window).height());
}
$(this).find('input[name=item_name]').trigger('focus');
$(this).find('input.datefield').each(function () {
addDatepicker($(this).css('width', '95%'), 'date');
});
$(this).find('input.datetimefield').each(function () {
addDatepicker($(this).css('width', '95%'), 'datetime');
});
// @ts-ignore
$.datepicker.initialized = false;
},
close: function () {
$(this).remove();
}
});
/**
* @var mode Used to remember whether the editor is in
* "Edit" or "Add" mode
*/
var mode = 'add';
if ($('input[name=editor_process_edit]').length > 0) {
mode = 'edit';
}
// Attach syntax highlighted editor to the definition
/**
* @var elm jQuery object containing the reference to
* the Definition textarea.
*/
var $elm = $('textarea[name=item_definition]').last();
var linterOptions = {
editorType: 'event',
};
that.syntaxHiglighter = getSqlEditor($elm, {}, 'vertical', linterOptions);
window.codeMirrorEditor = that.syntaxHiglighter;
}); // end $.get()
const eventsEditorModal = document.getElementById('eventsEditorModal');
eventsEditorModal.addEventListener('shown.bs.modal', function () {
/**
* Issue #15810 - use button titles for modals (eg: new procedure)
* Respect the order: title on href tag, href content, title sent in response
*/
eventsEditorModal.querySelector('.modal-title').textContent = $this.attr('title') || $this.text() || $(data.title).text();
eventsEditorModal.querySelector('.modal-body').innerHTML = data.message;
const eventsEditorModalSaveButton = document.getElementById('eventsEditorModalSaveButton');
eventsEditorModalSaveButton?.addEventListener('click', eventsEditorModalSaveEventHandler);
$(this).find('input[name=item_name]').trigger('focus');
$(this).find('input.datefield').each(function () {
addDatepicker($(this).css('width', '95%'), 'date');
});
$(this).find('input.datetimefield').each(function () {
addDatepicker($(this).css('width', '95%'), 'datetime');
});
// @ts-ignore
$.datepicker.initialized = false;
if ($('input[name=editor_process_edit]').length > 0) {
isEditMode = true;
}
// Attach syntax highlighted editor to the definition
/**
* @var elm jQuery object containing the reference to
* the Definition textarea.
*/
var $elm = $('textarea[name=item_definition]').last();
var linterOptions = {
editorType: 'event',
};
that.syntaxHiglighter = getSqlEditor($elm, {}, 'vertical', linterOptions);
window.codeMirrorEditor = that.syntaxHiglighter;
});
eventsEditorModal.addEventListener('hidden.bs.modal', function () {
const eventsEditorModalSaveButton = document.getElementById('eventsEditorModalSaveButton');
eventsEditorModalSaveButton?.removeEventListener('click', eventsEditorModalSaveEventHandler);
document.getElementById('eventsEditorModal').querySelector('.modal-body').innerHTML = '<div class="spinner-border" role="status">' +
'<span class="visually-hidden">' + window.Messages.strLoading + '</span></div>';
});
window.bootstrap.Modal.getOrCreateInstance(eventsEditorModal).show();
});
},
dropDialog: function ($this) {
@ -561,9 +528,10 @@ const DatabaseEvents = {
* to an element that is being validated
*/
var $elm = null;
if (this.$ajaxDialog.find('select[name=item_type]').find(':selected').val() === 'RECURRING') {
const eventsEditorModal = $('#eventsEditorModal');
if (eventsEditorModal.find('select[name=item_type]').find(':selected').val() === 'RECURRING') {
// The interval field must not be empty for recurring events
$elm = this.$ajaxDialog.find('input[name=item_interval_value]');
$elm = eventsEditorModal.find('input[name=item_interval_value]');
if ($elm.val() === '') {
$elm.trigger('focus');
alert(window.Messages.strFormEmpty);
@ -572,7 +540,7 @@ const DatabaseEvents = {
}
} else {
// The execute_at field must not be empty for "once off" events
$elm = this.$ajaxDialog.find('input[name=item_execute_at]');
$elm = eventsEditorModal.find('input[name=item_execute_at]');
if ($elm.val() === '') {
$elm.trigger('focus');
alert(window.Messages.strFormEmpty);

View File

@ -153,4 +153,24 @@
</div>
</div>
</div>
<div class="modal fade" id="eventsEditorModal" tabindex="-1" aria-labelledby="eventsEditorModalLabel" aria-hidden="true">
<div class="modal-dialog modal-xl modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="eventsEditorModalLabel">{{ t('Event editor') }}</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="{{ t('Close') }}"></button>
</div>
<div class="modal-body">
<div class="spinner-border" role="status">
<span class="visually-hidden">{{ t('Loading…') }}</span>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{{ t('Close') }}</button>
<button type="button" class="btn btn-primary" id="eventsEditorModalSaveButton">{{ t('Save changes') }}</button>
</div>
</div>
</div>
</div>
</div>

View File

@ -181,6 +181,26 @@ final class EventsControllerTest extends AbstractTestCase
</div>
</div>
</div>
<div class="modal fade" id="eventsEditorModal" tabindex="-1" aria-labelledby="eventsEditorModalLabel" aria-hidden="true">
<div class="modal-dialog modal-xl modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="eventsEditorModalLabel">Event editor</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="spinner-border" role="status">
<span class="visually-hidden">Loading…</span>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary" id="eventsEditorModalSaveButton">Save changes</button>
</div>
</div>
</div>
</div>
</div>
HTML;
@ -307,6 +327,26 @@ HTML;
</div>
</div>
</div>
<div class="modal fade" id="eventsEditorModal" tabindex="-1" aria-labelledby="eventsEditorModalLabel" aria-hidden="true">
<div class="modal-dialog modal-xl modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="eventsEditorModalLabel">Event editor</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="spinner-border" role="status">
<span class="visually-hidden">Loading…</span>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary" id="eventsEditorModalSaveButton">Save changes</button>
</div>
</div>
</div>
</div>
</div>
HTML;