update static files

This commit is contained in:
2026-05-06 06:42:37 -06:00
parent 455d684bd4
commit 4ce19c5e40
47 changed files with 1146 additions and 775 deletions

View File

@@ -15,6 +15,8 @@ Requires core.js and SelectBox.js.
const from_box = document.getElementById(field_id);
from_box.id += '_from'; // change its ID
from_box.className = 'filtered';
from_box.setAttribute('aria-labelledby', field_id + '_from_label');
from_box.setAttribute('aria-describedby', `${field_id}_helptext ${field_id}_choose_helptext`);
for (const p of from_box.parentNode.getElementsByTagName('p')) {
if (p.classList.contains("info")) {
@@ -38,18 +40,23 @@ Requires core.js and SelectBox.js.
// <div class="selector-available">
const selector_available = quickElement('div', selector_div);
selector_available.className = 'selector-available';
const title_available = quickElement('h2', selector_available, interpolate(gettext('Available %s') + ' ', [field_name]));
const selector_available_title = quickElement('div', selector_available);
selector_available_title.id = field_id + '_from_title';
selector_available_title.className = 'selector-available-title';
quickElement(
'span', title_available, '',
'class', 'help help-tooltip help-icon',
'title', interpolate(
gettext(
'This is the list of available %s. You may choose some by ' +
'selecting them in the box below and then clicking the ' +
'"Choose" arrow between the two boxes.'
),
[field_name]
)
'label',
selector_available_title,
interpolate(gettext('Available %s') + ' ', [field_name]),
'id',
field_id + '_from_label',
'for',
field_id + '_from'
);
quickElement(
'p',
selector_available_title,
interpolate(gettext('Choose %s by selecting them and then select the "Choose" arrow button.'), [field_name]),
'id', `${field_id}_choose_helptext`, 'class', 'helptext'
);
const filter_p = quickElement('p', selector_available, '', 'id', field_id + '_filter');
@@ -60,7 +67,7 @@ Requires core.js and SelectBox.js.
quickElement(
'span', search_filter_label, '',
'class', 'help-tooltip search-label-icon',
'title', interpolate(gettext("Type into this box to filter down the list of available %s."), [field_name])
'aria-label', interpolate(gettext("Type into this box to filter down the list of available %s."), [field_name])
);
filter_p.appendChild(document.createTextNode(' '));
@@ -69,32 +76,55 @@ Requires core.js and SelectBox.js.
filter_input.id = field_id + '_input';
selector_available.appendChild(from_box);
const choose_all = quickElement('a', selector_available, gettext('Choose all'), 'title', interpolate(gettext('Click to choose all %s at once.'), [field_name]), 'href', '#', 'id', field_id + '_add_all_link');
choose_all.className = 'selector-chooseall';
const choose_all = quickElement(
'button',
selector_available,
interpolate(gettext('Choose all %s'), [field_name]),
'id', field_id + '_add_all',
'class', 'selector-chooseall',
'type', 'button'
);
// <ul class="selector-chooser">
const selector_chooser = quickElement('ul', selector_div);
selector_chooser.className = 'selector-chooser';
const add_link = quickElement('a', quickElement('li', selector_chooser), gettext('Choose'), 'title', gettext('Choose'), 'href', '#', 'id', field_id + '_add_link');
add_link.className = 'selector-add';
const remove_link = quickElement('a', quickElement('li', selector_chooser), gettext('Remove'), 'title', gettext('Remove'), 'href', '#', 'id', field_id + '_remove_link');
remove_link.className = 'selector-remove';
const add_button = quickElement(
'button',
quickElement('li', selector_chooser),
interpolate(gettext('Choose selected %s'), [field_name]),
'id', field_id + '_add',
'class', 'selector-add',
'type', 'button'
);
const remove_button = quickElement(
'button',
quickElement('li', selector_chooser),
interpolate(gettext('Remove selected %s'), [field_name]),
'id', field_id + '_remove',
'class', 'selector-remove',
'type', 'button'
);
// <div class="selector-chosen">
const selector_chosen = quickElement('div', selector_div, '', 'id', field_id + '_selector_chosen');
selector_chosen.className = 'selector-chosen';
const title_chosen = quickElement('h2', selector_chosen, interpolate(gettext('Chosen %s') + ' ', [field_name]));
const selector_chosen_title = quickElement('div', selector_chosen);
selector_chosen_title.className = 'selector-chosen-title';
selector_chosen_title.id = field_id + '_to_title';
quickElement(
'span', title_chosen, '',
'class', 'help help-tooltip help-icon',
'title', interpolate(
gettext(
'This is the list of chosen %s. You may remove some by ' +
'selecting them in the box below and then clicking the ' +
'"Remove" arrow between the two boxes.'
),
[field_name]
)
'label',
selector_chosen_title,
interpolate(gettext('Chosen %s') + ' ', [field_name]),
'id',
field_id + '_to_label',
'for',
field_id + '_to'
);
quickElement(
'p',
selector_chosen_title,
interpolate(gettext('Remove %s by selecting them and then select the "Remove" arrow button.'), [field_name]),
'id', `${field_id}_remove_helptext`, 'class', 'helptext'
);
const filter_selected_p = quickElement('p', selector_chosen, '', 'id', field_id + '_filter_selected');
@@ -105,7 +135,7 @@ Requires core.js and SelectBox.js.
quickElement(
'span', search_filter_selected_label, '',
'class', 'help-tooltip search-label-icon',
'title', interpolate(gettext("Type into this box to filter down the list of selected %s."), [field_name])
'aria-label', interpolate(gettext("Type into this box to filter down the list of selected %s."), [field_name])
);
filter_selected_p.appendChild(document.createTextNode(' '));
@@ -113,21 +143,35 @@ Requires core.js and SelectBox.js.
const filter_selected_input = quickElement('input', filter_selected_p, '', 'type', 'text', 'placeholder', gettext("Filter"));
filter_selected_input.id = field_id + '_selected_input';
const to_box = quickElement('select', selector_chosen, '', 'id', field_id + '_to', 'multiple', '', 'size', from_box.size, 'name', from_box.name);
to_box.className = 'filtered';
quickElement(
'select',
selector_chosen,
'',
'id', field_id + '_to',
'multiple', '',
'size', from_box.size,
'name', from_box.name,
'aria-labelledby', field_id + '_to_label',
'aria-describedby', `${field_id}_helptext ${field_id}_remove_helptext`,
'class', 'filtered'
);
const warning_footer = quickElement('div', selector_chosen, '', 'class', 'list-footer-display');
quickElement('span', warning_footer, '', 'id', field_id + '_list-footer-display-text');
quickElement('span', warning_footer, ' (click to clear)', 'class', 'list-footer-display__clear');
const clear_all = quickElement('a', selector_chosen, gettext('Remove all'), 'title', interpolate(gettext('Click to remove all chosen %s at once.'), [field_name]), 'href', '#', 'id', field_id + '_remove_all_link');
clear_all.className = 'selector-clearall';
quickElement('span', warning_footer, ' ' + gettext('(click to clear)'), 'class', 'list-footer-display__clear');
const clear_all = quickElement(
'button',
selector_chosen,
interpolate(gettext('Remove all %s'), [field_name]),
'id', field_id + '_remove_all',
'class', 'selector-clearall',
'type', 'button'
);
from_box.name = from_box.name + '_old';
// Set up the JavaScript event handlers for the select box filter interface
const move_selection = function(e, elem, move_func, from, to) {
if (elem.classList.contains('active')) {
if (!elem.hasAttribute('disabled')) {
move_func(from, to);
SelectFilter.refresh_icons(field_id);
SelectFilter.refresh_filtered_selects(field_id);
@@ -138,10 +182,10 @@ Requires core.js and SelectBox.js.
choose_all.addEventListener('click', function(e) {
move_selection(e, this, SelectBox.move_all, field_id + '_from', field_id + '_to');
});
add_link.addEventListener('click', function(e) {
add_button.addEventListener('click', function(e) {
move_selection(e, this, SelectBox.move, field_id + '_from', field_id + '_to');
});
remove_link.addEventListener('click', function(e) {
remove_button.addEventListener('click', function(e) {
move_selection(e, this, SelectBox.move, field_id + '_to', field_id + '_from');
});
clear_all.addEventListener('click', function(e) {
@@ -226,13 +270,12 @@ Requires core.js and SelectBox.js.
refresh_icons: function(field_id) {
const from = document.getElementById(field_id + '_from');
const to = document.getElementById(field_id + '_to');
// Active if at least one item is selected
document.getElementById(field_id + '_add_link').classList.toggle('active', SelectFilter.any_selected(from));
document.getElementById(field_id + '_remove_link').classList.toggle('active', SelectFilter.any_selected(to));
// Active if the corresponding box isn't empty
document.getElementById(field_id + '_add_all_link').classList.toggle('active', from.querySelector('option'));
document.getElementById(field_id + '_remove_all_link').classList.toggle('active', to.querySelector('option'));
SelectFilter.refresh_filtered_warning(field_id);
// Disabled if no items are selected.
document.getElementById(field_id + '_add').disabled = !SelectFilter.any_selected(from);
document.getElementById(field_id + '_remove').disabled = !SelectFilter.any_selected(to);
// Disabled if the corresponding box is empty.
document.getElementById(field_id + '_add_all').disabled = !from.querySelector('option');
document.getElementById(field_id + '_remove_all').disabled = !to.querySelector('option');
},
filter_key_press: function(event, field_id, source, target) {
const source_box = document.getElementById(field_id + source);

View File

@@ -91,7 +91,10 @@
message = interpolate(message, [timezoneOffset]);
const warning = document.createElement('div');
const id = inp.id;
const field_id = inp.closest('p.datetime') ? id.slice(0, id.lastIndexOf("_")) : id;
warning.classList.add('help', warningClass);
warning.id = `${field_id}_timezone_warning_helptext`;
warning.textContent = message;
inp.parentNode.appendChild(warning);
},
@@ -108,6 +111,7 @@
const now_link = document.createElement('a');
now_link.href = "#";
now_link.textContent = gettext('Now');
now_link.role = 'button';
now_link.addEventListener('click', function(e) {
e.preventDefault();
DateTimeShortcuts.handleClockQuicklink(num, -1);
@@ -163,7 +167,7 @@
// where name is the name attribute of the <input>.
const name = typeof DateTimeShortcuts.clockHours[inp.name] === 'undefined' ? 'default_' : inp.name;
DateTimeShortcuts.clockHours[name].forEach(function(element) {
const time_link = quickElement('a', quickElement('li', time_list), gettext(element[0]), 'href', '#');
const time_link = quickElement('a', quickElement('li', time_list), gettext(element[0]), 'role', 'button', 'href', '#');
time_link.addEventListener('click', function(e) {
e.preventDefault();
DateTimeShortcuts.handleClockQuicklink(num, element[1]);
@@ -172,7 +176,7 @@
const cancel_p = quickElement('p', clock_box);
cancel_p.className = 'calendar-cancel';
const cancel_link = quickElement('a', cancel_p, gettext('Cancel'), 'href', '#');
const cancel_link = quickElement('a', cancel_p, gettext('Cancel'), 'role', 'button', 'href', '#');
cancel_link.addEventListener('click', function(e) {
e.preventDefault();
DateTimeShortcuts.dismissClock(num);
@@ -235,6 +239,7 @@
inp.parentNode.insertBefore(shortcuts_span, inp.nextSibling);
const today_link = document.createElement('a');
today_link.href = '#';
today_link.role = 'button';
today_link.appendChild(document.createTextNode(gettext('Today')));
today_link.addEventListener('click', function(e) {
e.preventDefault();
@@ -309,19 +314,19 @@
// calendar shortcuts
const shortcuts = quickElement('div', cal_box);
shortcuts.className = 'calendar-shortcuts';
let day_link = quickElement('a', shortcuts, gettext('Yesterday'), 'href', '#');
let day_link = quickElement('a', shortcuts, gettext('Yesterday'), 'role', 'button', 'href', '#');
day_link.addEventListener('click', function(e) {
e.preventDefault();
DateTimeShortcuts.handleCalendarQuickLink(num, -1);
});
shortcuts.appendChild(document.createTextNode('\u00A0|\u00A0'));
day_link = quickElement('a', shortcuts, gettext('Today'), 'href', '#');
day_link = quickElement('a', shortcuts, gettext('Today'), 'role', 'button', 'href', '#');
day_link.addEventListener('click', function(e) {
e.preventDefault();
DateTimeShortcuts.handleCalendarQuickLink(num, 0);
});
shortcuts.appendChild(document.createTextNode('\u00A0|\u00A0'));
day_link = quickElement('a', shortcuts, gettext('Tomorrow'), 'href', '#');
day_link = quickElement('a', shortcuts, gettext('Tomorrow'), 'role', 'button', 'href', '#');
day_link.addEventListener('click', function(e) {
e.preventDefault();
DateTimeShortcuts.handleCalendarQuickLink(num, +1);
@@ -330,7 +335,7 @@
// cancel bar
const cancel_p = quickElement('p', cal_box);
cancel_p.className = 'calendar-cancel';
const cancel_link = quickElement('a', cancel_p, gettext('Cancel'), 'href', '#');
const cancel_link = quickElement('a', cancel_p, gettext('Cancel'), 'role', 'button', 'href', '#');
cancel_link.addEventListener('click', function(e) {
e.preventDefault();
DateTimeShortcuts.dismissCalendar(num);

View File

@@ -55,8 +55,9 @@
if (elem.classList.contains('vManyToManyRawIdAdminField') && elem.value) {
elem.value += ',' + chosenId;
} else {
document.getElementById(name).value = chosenId;
elem.value = chosenId;
}
$(elem).trigger('change');
const index = relatedWindows.indexOf(win);
if (index > -1) {
relatedWindows.splice(index, 1);
@@ -87,7 +88,7 @@
}
}
function updateRelatedSelectsOptions(currentSelect, win, objId, newRepr, newId) {
function updateRelatedSelectsOptions(currentSelect, win, objId, newRepr, newId, skipIds = []) {
// After create/edit a model from the options next to the current
// select (+ or :pencil:) update ForeignKey PK of the rest of selects
// in the page.
@@ -100,7 +101,7 @@
const selectsRelated = document.querySelectorAll(`[data-model-ref="${modelName}"] [data-context="available-source"]`);
selectsRelated.forEach(function(select) {
if (currentSelect === select) {
if (currentSelect === select || skipIds && skipIds.includes(select.id)) {
return;
}
@@ -109,6 +110,11 @@
if (!option) {
option = new Option(newRepr, newId);
select.options.add(option);
// Update SelectBox cache for related fields.
if (window.SelectBox !== undefined && !SelectBox.cache[currentSelect.id]) {
SelectBox.add_to_cache(select.id, option);
SelectBox.redisplay(select.id);
}
return;
}
@@ -136,9 +142,14 @@
$(elem).trigger('change');
} else {
const toId = name + "_to";
const toElem = document.getElementById(toId);
const o = new Option(newRepr, newId);
SelectBox.add_to_cache(toId, o);
SelectBox.redisplay(toId);
if (toElem && toElem.nodeName.toUpperCase() === 'SELECT') {
const skipIds = [name + "_from"];
updateRelatedSelectsOptions(toElem, win, null, newRepr, newId, skipIds);
}
}
const index = relatedWindows.indexOf(win);
if (index > -1) {
@@ -195,6 +206,7 @@
window.dismissChangeRelatedObjectPopup = dismissChangeRelatedObjectPopup;
window.dismissDeleteRelatedObjectPopup = dismissDeleteRelatedObjectPopup;
window.dismissChildPopups = dismissChildPopups;
window.relatedWindows = relatedWindows;
// Kept for backward compatibility
window.showAddAnotherPopup = showRelatedObjectPopup;

View File

@@ -160,7 +160,7 @@ depends on core.js for utility functions like removeChildren or quickElement
}
const cell = quickElement('td', tableRow, '', 'class', todayClass);
const link = quickElement('a', cell, currentDay, 'href', '#');
const link = quickElement('a', cell, currentDay, 'role', 'button', 'href', '#');
link.addEventListener('click', calendarMonth(year, month));
currentDay++;
}

View File

@@ -50,11 +50,11 @@
// If forms are laid out as table rows, insert the
// "add" button in a new table row:
const numCols = $this.eq(-1).children().length;
$parent.append('<tr class="' + options.addCssClass + '"><td colspan="' + numCols + '"><a href="#">' + options.addText + "</a></tr>");
$parent.append('<tr class="' + options.addCssClass + '"><td colspan="' + numCols + '"><a role="button" class="addlink" href="#">' + options.addText + "</a></tr>");
addButton = $parent.find("tr:last a");
} else {
// Otherwise, insert it immediately after the last form:
$this.filter(":last").after('<div class="' + options.addCssClass + '"><a href="#">' + options.addText + "</a></div>");
$this.filter(":last").after('<div class="' + options.addCssClass + '"><a role="button" class="addlink" href="#">' + options.addText + "</a></div>");
addButton = $this.filter(":last").next().find("a");
}
}
@@ -104,15 +104,15 @@
if (row.is("tr")) {
// If the forms are laid out in table rows, insert
// the remove button into the last table cell:
row.children(":last").append('<div><a class="' + options.deleteCssClass + '" href="#">' + options.deleteText + "</a></div>");
row.children(":last").append('<div><a role="button" class="' + options.deleteCssClass + '" href="#">' + options.deleteText + "</a></div>");
} else if (row.is("ul") || row.is("ol")) {
// If they're laid out as an ordered/unordered list,
// insert an <li> after the last list item:
row.append('<li><a class="' + options.deleteCssClass + '" href="#">' + options.deleteText + "</a></li>");
row.append('<li><a role="button" class="' + options.deleteCssClass + '" href="#">' + options.deleteText + "</a></li>");
} else {
// Otherwise, just insert the remove button as the
// last child element of the form's container:
row.children(":first").append('<span><a class="' + options.deleteCssClass + '" href="#">' + options.deleteText + "</a></span>");
row.children(":first").append('<span><a role="button" class="' + options.deleteCssClass + '" href="#">' + options.deleteText + "</a></span>");
}
// Add delete handler for each row.
row.find("a." + options.deleteCssClass).on('click', inlineDeleteHandler.bind(this));