From 62e557d25a2aebf5d61e5286cc927a6aeb99492c Mon Sep 17 00:00:00 2001 From: DhanyaShah Date: Fri, 6 Dec 2024 19:03:05 -0500 Subject: [PATCH] Fix icon refresh and formset handling- reinitialized Selectfilter --- .../admin/static/admin/js/SelectFilter2.js | 45 ++++++++++++++++--- .../admin/static/admin/js/autocomplete.js | 11 +++++ .../contrib/admin/static/admin/js/inlines.js | 5 +++ django/contrib/admin/widgets.py | 12 ++--- 4 files changed, 62 insertions(+), 11 deletions(-) diff --git a/django/contrib/admin/static/admin/js/SelectFilter2.js b/django/contrib/admin/static/admin/js/SelectFilter2.js index 7f0cfef8c9..74e1655eea 100644 --- a/django/contrib/admin/static/admin/js/SelectFilter2.js +++ b/django/contrib/admin/static/admin/js/SelectFilter2.js @@ -248,12 +248,31 @@ 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').classList.toggle('active', SelectFilter.any_selected(from)); - document.getElementById(field_id + '_remove').classList.toggle('active', SelectFilter.any_selected(to)); - // Active if the corresponding box isn't empty - document.getElementById(field_id + '_add_all').classList.toggle('active', from.querySelector('option')); - document.getElementById(field_id + '_remove_all').classList.toggle('active', to.querySelector('option')); + + if (!from || !to) { + // If required elements are missing, skip refreshing icons. + console.warn(`Skipping icon refresh for field_id: ${field_id}. Elements are missing.`); + return; + } + + const addButton = document.getElementById(field_id + '_add'); + const removeButton = document.getElementById(field_id + '_remove'); + const addAllButton = document.getElementById(field_id + '_add_all'); + const removeAllButton = document.getElementById(field_id + '_remove_all'); + + if (addButton) { + addButton.classList.toggle('active', SelectFilter.any_selected(from)); + } + if (removeButton) { + removeButton.classList.toggle('active', SelectFilter.any_selected(to)); + } + if (addAllButton) { + addAllButton.classList.toggle('active', from.querySelector('option')); + } + if (removeAllButton) { + removeAllButton.classList.toggle('active', to.querySelector('option')); + } + SelectFilter.refresh_filtered_warning(field_id); }, filter_key_press: function(event, field_id, source, target) { @@ -305,4 +324,18 @@ Requires core.js and SelectBox.js. SelectFilter.init(el.id, data.fieldName, parseInt(data.isStacked, 10)); }); }); + + document.addEventListener('formset:removed', function(e) { + const inlineElement = e.target; // The removed inline element. + const field_id = inlineElement.querySelector('select.filtered')?.id; + if (field_id) { + // Remove any references or event listeners related to the field_id. + const fromBox = document.getElementById(field_id + '_from'); + const toBox = document.getElementById(field_id + '_to'); + if (fromBox) fromBox.remove(); + if (toBox) toBox.remove(); + console.log(`Cleaned up SelectFilter for field_id: ${field_id}`); + } + }); + } diff --git a/django/contrib/admin/static/admin/js/autocomplete.js b/django/contrib/admin/static/admin/js/autocomplete.js index d3daeab890..e052747a90 100644 --- a/django/contrib/admin/static/admin/js/autocomplete.js +++ b/django/contrib/admin/static/admin/js/autocomplete.js @@ -30,4 +30,15 @@ document.addEventListener('formset:added', (event) => { $(event.target).find('.admin-autocomplete').djangoAdminSelect2(); }); + + // document.addEventListener('formset:added', function(e) { + // const formId = e.target.id; // Get the form ID + // const newField = formId.querySelector('select.selectfilter'); + // if (newField) { + // const fieldName = newField.dataset.fieldName; + // const isStacked = parseInt(newField.dataset.isStacked, 10); + // SelectFilter.init(newField.id, fieldName, isStacked); + // } + // }); + } diff --git a/django/contrib/admin/static/admin/js/inlines.js b/django/contrib/admin/static/admin/js/inlines.js index cd3726cf30..6ae0f406a5 100644 --- a/django/contrib/admin/static/admin/js/inlines.js +++ b/django/contrib/admin/static/admin/js/inlines.js @@ -159,6 +159,11 @@ updateElementIndex($(forms).get(i), options.prefix, i); $(forms.get(i)).find("*").each(updateElementCallback); } + + // Reinitialize SelectFilter widgets to ensure proper functionality + if (typeof updateSelectFilter === "function") { + updateSelectFilter(); + } }; const toggleDeleteButtonVisibility = function(inlineGroup) { diff --git a/django/contrib/admin/widgets.py b/django/contrib/admin/widgets.py index 74004a7ba7..29b83634b0 100644 --- a/django/contrib/admin/widgets.py +++ b/django/contrib/admin/widgets.py @@ -38,14 +38,16 @@ class FilteredSelectMultiple(forms.SelectMultiple): self.verbose_name = verbose_name self.is_stacked = is_stacked super().__init__(attrs, choices) - + def get_context(self, name, value, attrs): context = super().get_context(name, value, attrs) - context["widget"]["attrs"]["class"] = "selectfilter" + widget_attrs = context["widget"]["attrs"] + widget_attrs["class"] = "selectfilter" if self.is_stacked: - context["widget"]["attrs"]["class"] += "stacked" - context["widget"]["attrs"]["data-field-name"] = self.verbose_name - context["widget"]["attrs"]["data-is-stacked"] = int(self.is_stacked) + widget_attrs["class"] += "stacked" + widget_attrs["data-field-name"] = self.verbose_name + widget_attrs["data-is-stacked"] = int(self.is_stacked) + widget_attrs["id"] = attrs.get("id", f"id_{name}") # Ensure 'id' is set return context