Code

Ticket #5899: 5899.collapsible-fieldsets.diff

File 5899.collapsible-fieldsets.diff, 8.6 KB (added by julien, 3 years ago)
Line 
1diff --git a/django/contrib/admin/helpers.py b/django/contrib/admin/helpers.py
2index 04a3492..fb5e5a6 100644
3--- a/django/contrib/admin/helpers.py
4+++ b/django/contrib/admin/helpers.py
5@@ -74,7 +74,7 @@ class Fieldset(object):
6         self.readonly_fields = readonly_fields
7 
8     def _media(self):
9-        if 'collapse' in self.classes:
10+        if 'collapse' in self.classes or 'collapsible' in self.classes:
11             js = ['jquery.min.js', 'jquery.init.js', 'collapse.min.js']
12             return forms.Media(js=[static('admin/js/%s' % url) for url in js])
13         return forms.Media()
14diff --git a/django/contrib/admin/static/admin/js/collapse.js b/django/contrib/admin/static/admin/js/collapse.js
15index 0a1e2d8..93b58ac 100644
16--- a/django/contrib/admin/static/admin/js/collapse.js
17+++ b/django/contrib/admin/static/admin/js/collapse.js
18@@ -1,27 +1,107 @@
19 (function($) {
20        $(document).ready(function() {
21-               // Add anchor tag for Show/Hide link
22-               $("fieldset.collapse").each(function(i, elem) {
23-                       // Don't hide if fields in this fieldset have errors
24-                       if ( $(elem).find("div.errors").length == 0 ) {
25-                               $(elem).addClass("collapsed");
26-                               $(elem).find("h2").first().append(' (<a id="fieldsetcollapser' +
27-                                       i +'" class="collapse-toggle" href="#">' + gettext("Show") +
28-                                       '</a>)');
29-                       }
30-               });
31-               // Add toggle to anchor tag
32-               $("fieldset.collapse a.collapse-toggle").toggle(
33-                       function() { // Show
34-                               $(this).text(gettext("Hide"));
35-                               $(this).closest("fieldset").removeClass("collapsed");
36+               /**
37+                * Structure for handling collapsible fieldsets.
38+                * There are two opportunities:
39+                * 1.   using "collapse" as class name of the fieldset the fieldset will
40+                *              be initially hidden and can be shown by click
41+                * 2.   using "collapsible" as class name of the fieldset the fielset
42+                *              will be initially shown but can be hidden by click.
43+                * This structure adds the click elements and their behaviour to the
44+                * existing html of the fieldsets.
45+                */
46+               var CollapsedFieldsets = {
47+                       /**
48+                        * Initializes the structure.
49+                        */
50+                       init: function() {
51+                               this.appendShowHideLink();
52+                               this.initToggling();
53+                       },
54+
55+                       /**
56+                        * Appends the clickable elements, either for showing or hiding
57+                        * depending on the current state of the fieldset.
58+                        */
59+                       appendShowHideLink: function() {
60+                               $('fieldset.collapse, fieldset.collapsible').each(function(i, elem) {
61+                                       // Don't hide if fields in this fieldset have errors
62+                                       if ( $(elem).find('div.errors').length == 0 ) {
63+                                               // do not collapse initially shown "collapsibles"
64+                                               if (!$(elem).hasClass('collapsible')) {
65+                                                       $(elem).addClass('collapsed');
66+                                               }
67+                                               $(elem).find('h2').first().append(' (<a id="fieldsetcollapser' +
68+                                                       i +'" class="collapse-toggle" href="#">' +
69+                                                       (!$(elem).hasClass('collapsible') ? gettext('Show') : gettext('Hide')) +
70+                                                       '</a>)');
71+                                       }
72+                               });
73+                       },
74+
75+                       /**
76+                        * Initializes the toggling for the clickable elements to show/hide
77+                        * the fieldset.
78+                        */
79+                       initToggling: function() {
80+                               $('fieldset.collapse a.collapse-toggle, fieldset.collapsible a.collapse-toggle').toggle(
81+                                       function(e) { this.toggleOn(e.target);  }.bind(this),
82+                                       function(e) { this.toggleOff(e.target); }.bind(this)
83+                               );
84+                       },
85+
86+                       /**
87+                        * The first toggle method moving the clickable element from its
88+                        * initial state to the next one. Collapses the fieldset if it's
89+                        * uncollapsed and vice versa.
90+                        * @param target        the clickable link
91+                        */
92+                       toggleOn: function(target) {
93+                               if ($(target).closest('fieldset').hasClass('collapsible')) {
94+                                       this.collapseElement($(target));
95+                               } else {
96+                                       this.uncollapseElement($(target));
97+                               }
98                                return false;
99                        },
100-                       function() { // Hide
101-                               $(this).text(gettext("Show"));
102-                               $(this).closest("fieldset").addClass("collapsed");
103+
104+                       /**
105+                        * The seconds toggle method moving the clickable element from its
106+                        * second state back to the initial one. Collapses the fieldset
107+                        * if it's uncollapsed and vice versa.
108+                        * @param target        the clickable link
109+                        */
110+                       toggleOff: function(target) {
111+                               if ($(target).closest('fieldset').hasClass('collapsible')) {
112+                                       this.uncollapseElement($(target));
113+                               } else {
114+                                       this.collapseElement($(target));
115+                               }
116                                return false;
117+                       },
118+
119+                       /**
120+                        * Collapses the given element by applying the new "Show" label
121+                        * and adding the collapsed class.
122+                        * @param element       the clickable element
123+                        */
124+                       collapseElement: function(element) {
125+                               element.text(gettext('Show'));
126+                               element.closest('fieldset').addClass('collapsed');
127+                       },
128+
129+                       /**
130+                        * Uncollapses the given element by applying the new "Hide" label
131+                        * and removing the collapsed class.
132+                        * @param element       the clickable element
133+                        */
134+                       uncollapseElement: function(element) {
135+                               element.text(gettext('Hide'));
136+                               element.closest('fieldset').removeClass('collapsed');
137                        }
138-               );
139+               };
140+
141+               // Engage!
142+               CollapsedFieldsets.init();
143        });
144 })(django.jQuery);
145diff --git a/django/contrib/admin/static/admin/js/collapse.min.js b/django/contrib/admin/static/admin/js/collapse.min.js
146index 428984e..300302a 100644
147--- a/django/contrib/admin/static/admin/js/collapse.min.js
148+++ b/django/contrib/admin/static/admin/js/collapse.min.js
149@@ -1,2 +1,2 @@
150-(function(a){a(document).ready(function(){a("fieldset.collapse").each(function(c,b){if(a(b).find("div.errors").length==0){a(b).addClass("collapsed");a(b).find("h2").first().append(' (<a id="fieldsetcollapser'+c+'" class="collapse-toggle" href="#">'+gettext("Show")+"</a>)")}});a("fieldset.collapse a.collapse-toggle").toggle(function(){a(this).text(gettext("Hide"));a(this).closest("fieldset").removeClass("collapsed");return false},function(){a(this).text(gettext("Show"));a(this).closest("fieldset").addClass("collapsed");
151-return false})})})(django.jQuery);
152+(function(b){b(document).ready(function(){({init:function(){this.appendShowHideLink();this.initToggling()},appendShowHideLink:function(){b("fieldset.collapse, fieldset.collapsible").each(function(a,c){if(b(c).find("div.errors").length==0){b(c).hasClass("collapsible")||b(c).addClass("collapsed");b(c).find("h2").first().append(' (<a id="fieldsetcollapser'+a+'" class="collapse-toggle" href="#">'+(!b(c).hasClass("collapsible")?gettext("Show"):gettext("Hide"))+"</a>)")}})},initToggling:function(){b("fieldset.collapse a.collapse-toggle, fieldset.collapsible a.collapse-toggle").toggle(function(a){this.toggleOn(a.target)}.bind(this),
153+function(a){this.toggleOff(a.target)}.bind(this))},toggleOn:function(a){b(a).closest("fieldset").hasClass("collapsible")?this.collapseElement(b(a)):this.uncollapseElement(b(a));return false},toggleOff:function(a){b(a).closest("fieldset").hasClass("collapsible")?this.uncollapseElement(b(a)):this.collapseElement(b(a));return false},collapseElement:function(a){a.text(gettext("Show"));a.closest("fieldset").addClass("collapsed")},uncollapseElement:function(a){a.text(gettext("Hide"));a.closest("fieldset").removeClass("collapsed")}}).init()})})(django.jQuery);
154diff --git a/docs/intro/tutorial02.txt b/docs/intro/tutorial02.txt
155index 7d2ce4f..3bd887c 100644
156--- a/docs/intro/tutorial02.txt
157+++ b/docs/intro/tutorial02.txt
158@@ -212,9 +212,10 @@ Here's what our form looks like now:
159    :alt: Form has fieldsets now
160 
161 You can assign arbitrary HTML classes to each fieldset. Django provides a
162-``"collapse"`` class that displays a particular fieldset initially collapsed.
163-This is useful when you have a long form that contains a number of fields that
164-aren't commonly used::
165+``"collapse"`` and ``"collapsible"` classes that allow a particular fieldset to
166+be initially collapsed, respectively to be collapsible. This is useful when
167+you have a long form that contains a number of fields that aren't commonly
168+used::
169 
170         class PollAdmin(admin.ModelAdmin):
171             fieldsets = [
172diff --git a/docs/ref/contrib/admin/index.txt b/docs/ref/contrib/admin/index.txt
173index 8ba41aa..5c7a406 100644
174--- a/docs/ref/contrib/admin/index.txt
175+++ b/docs/ref/contrib/admin/index.txt
176@@ -274,7 +274,9 @@ subclass::
177             Two useful classes defined by the default admin site stylesheet are
178             ``collapse`` and ``wide``. Fieldsets with the ``collapse`` style
179             will be initially collapsed in the admin and replaced with a small
180-            "click to expand" link. Fieldsets with the ``wide`` style will be
181+            "click to expand" link. Alternatively you can use ``collapsible``
182+            which has the same effect as ``collapse``, but fieldsets will be
183+            initially expanded. Fieldsets with the ``wide`` style will be
184             given extra horizontal space.
185 
186         * ``description``