diff options
Diffstat (limited to 'django/contrib/admin/static')
3 files changed, 53 insertions, 15 deletions
diff --git a/django/contrib/admin/static/admin/js/SelectBox.js b/django/contrib/admin/static/admin/js/SelectBox.js index 3db4ec7fa6..17c182c53f 100644 --- a/django/contrib/admin/static/admin/js/SelectBox.js +++ b/django/contrib/admin/static/admin/js/SelectBox.js @@ -1,5 +1,6 @@ 'use strict'; { + const getOptionGroupName = (option) => option.parentElement.label; const SelectBox = { cache: {}, init: function(id) { @@ -7,7 +8,12 @@ SelectBox.cache[id] = []; const cache = SelectBox.cache[id]; for (const node of box.options) { - cache.push({value: node.value, text: node.text, displayed: 1}); + const group = getOptionGroupName(node); + cache.push({group, value: node.value, text: node.text, displayed: 1}); + } + // Only sort if there are any groups (to preserve existing behavior for non-grouped selects) + if (cache.some(item => item.group)) { + SelectBox.sort(id); } }, redisplay: function(id) { @@ -15,12 +21,25 @@ const box = document.getElementById(id); const scroll_value_from_top = box.scrollTop; box.innerHTML = ''; - for (const node of SelectBox.cache[id]) { - if (node.displayed) { - const new_option = new Option(node.text, node.value, false, false); + let node = box; + let currentOptgroup = null; + for (const option of SelectBox.cache[id]) { + if (option.displayed) { + // Create a new optgroup when the group changes + if (option.group && option.group !== currentOptgroup) { + currentOptgroup = option.group; + node = document.createElement('optgroup'); + node.setAttribute('label', option.group); + box.appendChild(node); + } else if (!option.group && currentOptgroup !== null) { + // Back to ungrouped options + currentOptgroup = null; + node = box; + } + const new_option = new Option(option.text, option.value, false, false); // Shows a tooltip when hovering over the option - new_option.title = node.text; - box.appendChild(new_option); + new_option.title = option.text; + node.appendChild(new_option); } } box.scrollTop = scroll_value_from_top; @@ -57,7 +76,7 @@ cache.splice(delete_index, 1); }, add_to_cache: function(id, option) { - SelectBox.cache[id].push({value: option.value, text: option.text, displayed: 1}); + SelectBox.cache[id].push({group: option.group, value: option.value, text: option.text, displayed: 1}); }, cache_contains: function(id, value) { // Check if an item is contained in the cache @@ -73,10 +92,15 @@ for (const option of from_box.options) { const option_value = option.value; if (option.selected && SelectBox.cache_contains(from, option_value)) { - SelectBox.add_to_cache(to, {value: option_value, text: option.text, displayed: 1}); + const group = getOptionGroupName(option); + SelectBox.add_to_cache(to, {group, value: option_value, text: option.text, displayed: 1}); SelectBox.delete_from_cache(from, option_value); } } + // Only sort if there are any groups (to preserve existing behavior for non-grouped selects) + if (SelectBox.cache[to].some(item => item.group)) { + SelectBox.sort(to); + } SelectBox.redisplay(from); SelectBox.redisplay(to); }, @@ -85,17 +109,22 @@ for (const option of from_box.options) { const option_value = option.value; if (SelectBox.cache_contains(from, option_value)) { - SelectBox.add_to_cache(to, {value: option_value, text: option.text, displayed: 1}); + const group = getOptionGroupName(option); + SelectBox.add_to_cache(to, {group, value: option_value, text: option.text, displayed: 1}); SelectBox.delete_from_cache(from, option_value); } } + // Only sort if there are any groups (to preserve existing behavior for non-grouped selects) + if (SelectBox.cache[to].some(item => item.group)) { + SelectBox.sort(to); + } SelectBox.redisplay(from); SelectBox.redisplay(to); }, sort: function(id) { SelectBox.cache[id].sort(function(a, b) { - a = a.text.toLowerCase(); - b = b.text.toLowerCase(); + a = (a.group && a.group.toLowerCase() || '') + a.text.toLowerCase(); + b = (b.group && b.group.toLowerCase() || '') + b.text.toLowerCase(); if (a > b) { return 1; } diff --git a/django/contrib/admin/static/admin/js/admin/RelatedObjectLookups.js b/django/contrib/admin/static/admin/js/admin/RelatedObjectLookups.js index 1fc03c6232..f366f30f7e 100644 --- a/django/contrib/admin/static/admin/js/admin/RelatedObjectLookups.js +++ b/django/contrib/admin/static/admin/js/admin/RelatedObjectLookups.js @@ -113,6 +113,10 @@ // Update SelectBox cache for related fields. if (window.SelectBox !== undefined && !SelectBox.cache[currentSelect.id]) { SelectBox.add_to_cache(select.id, option); + // Sort if there are any groups present + if (SelectBox.cache[select.id].some(item => item.group)) { + SelectBox.sort(select.id); + } SelectBox.redisplay(select.id); } return; @@ -123,7 +127,7 @@ }); } - function dismissAddRelatedObjectPopup(win, newId, newRepr) { + function dismissAddRelatedObjectPopup(win, newId, newRepr, optgroup) { const name = removePopupIndex(win.name); const elem = document.getElementById(name); if (elem) { @@ -143,8 +147,13 @@ } else { const toId = name + "_to"; const toElem = document.getElementById(toId); - const o = new Option(newRepr, newId); - SelectBox.add_to_cache(toId, o); + const newOption = new Option(newRepr, newId); + newOption.group = optgroup; + SelectBox.add_to_cache(toId, newOption); + // Sort if there are any groups present + if (SelectBox.cache[toId].some(item => item.group)) { + SelectBox.sort(toId); + } SelectBox.redisplay(toId); if (toElem && toElem.nodeName.toUpperCase() === 'SELECT') { const skipIds = [name + "_from"]; diff --git a/django/contrib/admin/static/admin/js/popup_response.js b/django/contrib/admin/static/admin/js/popup_response.js index fecf0f4798..bdd93a6eb5 100644 --- a/django/contrib/admin/static/admin/js/popup_response.js +++ b/django/contrib/admin/static/admin/js/popup_response.js @@ -9,7 +9,7 @@ opener.dismissDeleteRelatedObjectPopup(window, initData.value); break; default: - opener.dismissAddRelatedObjectPopup(window, initData.value, initData.obj); + opener.dismissAddRelatedObjectPopup(window, initData.value, initData.obj, initData.optgroup); break; } } |
