aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md1
-rw-r--r--templates/web/base/admin/body-form.html3
-rw-r--r--templates/web/base/admin/user-form.html2
-rw-r--r--templates/web/zurich/admin/body-form.html3
-rw-r--r--web/cobrands/fixmystreet/fixmystreet.js53
-rw-r--r--web/cobrands/sass/_base.scss3
-rw-r--r--web/cobrands/sass/_multiselect.scss8
-rw-r--r--web/vendor/jquery.multi-select.min.js18
8 files changed, 53 insertions, 38 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 221fa3ade..5f9e335a9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -48,6 +48,7 @@
- Report field pre-filling for inspectors configurable #1854
- Admins can now unban users #1881
- Council dashboard has date range for report generation #1885
+ - More JavaScript-enhanced `<select multiple>` elements #1589
- UK:
- Use SVG logo, inlined on front page. #1887
- Inline critical CSS on front page.
diff --git a/templates/web/base/admin/body-form.html b/templates/web/base/admin/body-form.html
index 55d0e500c..54445a282 100644
--- a/templates/web/base/admin/body-form.html
+++ b/templates/web/base/admin/body-form.html
@@ -75,8 +75,7 @@
</div>
<p>
<label for="area_ids">[% loc('Area covered') %]</label>
- <select class="form-control" name="area_ids" id="area_ids" multiple>
- <option value=""> -- [% loc('Select an area') %] -- </option>
+ <select class="form-control js-multiple" name="area_ids" id="area_ids" multiple data-none="-- [% loc('Select an area') %] --">
[% SET body_areas = body.areas %]
[% FOR area IN areas %]
[% SET aid = area.id %]
diff --git a/templates/web/base/admin/user-form.html b/templates/web/base/admin/user-form.html
index 7b27f7497..9a2c0b9e2 100644
--- a/templates/web/base/admin/user-form.html
+++ b/templates/web/base/admin/user-form.html
@@ -127,7 +127,7 @@
</div>
[% IF c.user.is_superuser %]
[% loc('Trusted by bodies:') %]<br />
- <select class="form-control" id='body' name='trusted_bodies' multiple>
+ <select class="form-control js-multiple" id='body' name='trusted_bodies' multiple>
[% FOR body IN bodies %]
<option value="[% body.id %]"[% ' selected' IF user.has_permission_to('trusted', body.id) %]>[% body.name %]</option>
[% END %]
diff --git a/templates/web/zurich/admin/body-form.html b/templates/web/zurich/admin/body-form.html
index de9fac0e2..44adcbc72 100644
--- a/templates/web/zurich/admin/body-form.html
+++ b/templates/web/zurich/admin/body-form.html
@@ -22,8 +22,7 @@
<p>
<label for="area_ids">[% loc('Area covered') %]</label>
- <select class="form-control" name="area_ids" id="area_ids" multiple>
- <option value=""> -- [% loc('Select an area') %] -- </option>
+ <select class="form-control js-multiple" name="area_ids" id="area_ids" multiple data-none="-- [% loc('Select an area') %] --">
[% SET body_areas = body.areas %]
[% FOR area IN areas %]
[% SET aid = area.id %]
diff --git a/web/cobrands/fixmystreet/fixmystreet.js b/web/cobrands/fixmystreet/fixmystreet.js
index f73792a3c..16ee7b511 100644
--- a/web/cobrands/fixmystreet/fixmystreet.js
+++ b/web/cobrands/fixmystreet/fixmystreet.js
@@ -112,27 +112,38 @@ function isR2L() {
},
make_multi: function() {
- var $this = $(this),
- all = $this.data('all'),
- none = $this.data('none') || all,
- allOpts = $this.data('all-options') || [],
- extra = $this.data('extra'),
- extraOpts = $this.data('extra-options') || [];
-
- var presets = [{
- name: all,
- options: allOpts
- }];
-
- if (extra) {
- presets.push({name: extra, options: extraOpts});
- }
- $this.multiSelect({
- allText: all,
- noneText: none,
- positionMenuWithin: $('#side'),
- presets: presets
- });
+ // A convenience wrapper around $.multiSelect() that translates HTML
+ // data-* attributes into settings for the multiSelect constructor.
+ var $select = $(this);
+ var settings = {};
+
+ if ( $select.data('none') ) {
+ settings.noneText = $select.data('none');
+ }
+
+ if ( $select.data('all') ) {
+ settings.allText = $select.data('all');
+ settings.noneText = settings.noneText || settings.allText;
+ settings.presets = [];
+ settings.presets.push({
+ name: settings.allText,
+ options: $select.data('all-options') || []
+ });
+ }
+
+ if ( $select.data('extra') && $select.data('extra-options') ) {
+ settings.presets = settings.presets || [];
+ settings.presets.push({
+ name: $select.data('extra'),
+ options: $select.data('extra-options')
+ });
+ }
+
+ if ( document.querySelector('#side') && document.querySelector('#side').contains($select[0]) ) {
+ settings.positionMenuWithin = $('#side');
+ }
+
+ $select.multiSelect(settings);
}
});
diff --git a/web/cobrands/sass/_base.scss b/web/cobrands/sass/_base.scss
index 2c4ce1e33..109a67d64 100644
--- a/web/cobrands/sass/_base.scss
+++ b/web/cobrands/sass/_base.scss
@@ -294,6 +294,9 @@ select.form-control {
&[multiple] {
height: auto;
}
+
+ // Prevent a flash of tall select elements
+ // before $.multiSelect() has replaced them.
.js &.js-multiple[multiple] {
height: 2.2em;
}
diff --git a/web/cobrands/sass/_multiselect.scss b/web/cobrands/sass/_multiselect.scss
index 9dda17fea..ba90c0d89 100644
--- a/web/cobrands/sass/_multiselect.scss
+++ b/web/cobrands/sass/_multiselect.scss
@@ -1,14 +1,15 @@
.multi-select-container {
position: relative;
+ display: inline-block; // shrink to width of .multi-select-button
}
.multi-select-menu {
position: absolute;
left: 0;
top: 0.8em;
- z-index: 1;
- float: left;
- min-width: 100%;
+ z-index: 2; // stack above mysoc-footer
+ float: left; // shrink to width of child elements
+ min-width: 100%; // always at least as wide as its .multi-select-button sibling
background: #fff;
margin: 1em 0;
border: 1px solid #aaa;
@@ -20,6 +21,7 @@
display: block;
font-size: 0.875em;
padding: 0.6em 1em 0.6em 30px;
+ margin: 0;
white-space: nowrap;
& + & {
diff --git a/web/vendor/jquery.multi-select.min.js b/web/vendor/jquery.multi-select.min.js
index 6c7cefced..015aee226 100644
--- a/web/vendor/jquery.multi-select.min.js
+++ b/web/vendor/jquery.multi-select.min.js
@@ -1,9 +1,9 @@
-(function(c){function f(a,b){this.b=c(a);this.a=c.extend({},g,b);this.H()}var g={containerHTML:'<div class="multi-select-container">',menuHTML:'<div class="multi-select-menu">',buttonHTML:'<span class="multi-select-button">',menuItemsHTML:'<div class="multi-select-menuitems">',menuItemHTML:'<label class="multi-select-menuitem">',presetsHTML:'<div class="multi-select-presets">',activeClass:"multi-select-container--open",noneText:"-- Select --",allText:void 0,presets:void 0,positionedMenuClass:"multi-select-container--positioned",
-positionMenuWithin:void 0};c.extend(f.prototype,{H:function(){this.v();this.G();this.A();this.w();this.B();this.J();this.K();this.b.hide()},v:function(){if(!1===this.b.is("select[multiple]"))throw Error("$.multiSelect only works on <select multiple> elements");},G:function(){this.l=c('label[for="'+this.b.attr("id")+'"]')},A:function(){this.c=c(this.a.containerHTML);this.b.data("multi-select-container",this.c);this.c.insertAfter(this.b)},w:function(){var a=this;this.g=c(this.a.buttonHTML);this.g.attr({role:"button",
-"aria-haspopup":"true",tabindex:0,"aria-label":this.l.eq(0).text()}).on("keydown.multiselect",function(b){b=b.which;13!==b&&32!==b||a.g.click()}).on("click.multiselect",function(){a.m()}).appendTo(this.c);this.b.on("change.multiselect",function(){a.o()});this.o()},o:function(){var a=[],b=[];this.b.children("option").each(function(){var d=c(this).text();a.push(d);c(this).is(":selected")&&b.push(c.trim(d))});this.g.empty();0==b.length?this.g.text(this.a.noneText):b.length===a.length&&this.a.allText?
-this.g.text(this.a.allText):this.g.text(b.join(", "))},B:function(){var a=this;this.f=c(this.a.menuHTML);this.f.attr({role:"menu"}).on("keyup.multiselect",function(b){27===b.which&&a.j()}).appendTo(this.c);this.D();this.a.presets&&this.F()},D:function(){var a=this;this.h=c(this.a.menuItemsHTML);this.f.append(this.h);this.b.on("change.multiselect",function(b,d){!0!==d&&a.s()});this.s()},s:function(){var a=this;this.h.empty();this.b.children("option").each(function(b,d){b=a.C(c(d),b);a.h.append(b)})},
-F:function(){var a=this;this.i=c(this.a.presetsHTML);this.f.prepend(this.i);c.each(this.a.presets,function(b,d){b=a.b.attr("name")+"_preset_"+b;var h=c(a.a.menuItemHTML).attr({"for":b,role:"menuitem"}).text(" "+d.name).appendTo(a.i);c("<input>").attr({type:"radio",name:a.b.attr("name")+"_presets",id:b}).prependTo(h).on("change.multiselect",function(){a.b.val(d.options);a.b.trigger("change")})});this.b.on("change.multiselect",function(){a.u()});this.u()},u:function(){var a=this;c.each(this.a.presets,
-function(b,d){b=a.b.attr("name")+"_preset_"+b;b=a.i.find("#"+b);a:{d=d.options||[];var c=a.b.val()||[];if(d.length!=c.length)d=!1;else{d.sort();c.sort();for(var e=0;e<d.length;e++)if(d[e]!==c[e]){d=!1;break a}d=!0}}d?b.prop("checked",!0):b.prop("checked",!1)})},C:function(a,b){var d=this.b.attr("name")+"_"+b;b=c(this.a.menuItemHTML).attr({"for":d,role:"menuitem"}).text(" "+a.text());d=c("<input>").attr({type:"checkbox",id:d,value:a.val()}).prependTo(b);a.is(":disabled")&&d.attr("disabled","disabled");
-a.is(":selected")&&d.prop("checked","checked");d.on("change.multiselect",function(){c(this).prop("checked")?a.prop("selected",!0):a.prop("selected",!1);a.trigger("change",[!0])});return b},J:function(){var a=this;c("html").on("click.multiselect",function(){a.j()});this.c.on("click.multiselect",function(a){a.stopPropagation()})},K:function(){var a=this;this.l.on("click.multiselect",function(b){b.preventDefault();b.stopPropagation();a.m()})},I:function(){c("html").trigger("click.multiselect");this.c.addClass(this.a.activeClass);
-if(this.a.positionMenuWithin&&this.a.positionMenuWithin instanceof c){var a=this.f.offset().left+this.f.outerWidth(),b=this.a.positionMenuWithin.offset().left+this.a.positionMenuWithin.outerWidth();a>b&&(this.f.css("width",b-this.f.offset().left),this.c.addClass(this.a.positionedMenuClass))}},j:function(){this.c.removeClass(this.a.activeClass);this.c.removeClass(this.a.positionedMenuClass);this.f.css("width","auto")},m:function(){this.c.hasClass(this.a.activeClass)?this.j():this.I()}});c.fn.multiSelect=
-function(a){return this.each(function(){c.data(this,"plugin_multiSelect")||c.data(this,"plugin_multiSelect",new f(this,a))})}})(jQuery);
+(function(c){function f(b,a){this.b=c(b);this.a=c.extend({},g,a);this.H()}var g={containerHTML:'<div class="multi-select-container">',menuHTML:'<div class="multi-select-menu">',buttonHTML:'<span class="multi-select-button">',menuItemsHTML:'<div class="multi-select-menuitems">',menuItemHTML:'<label class="multi-select-menuitem">',presetsHTML:'<div class="multi-select-presets">',activeClass:"multi-select-container--open",noneText:"-- Select --",allText:void 0,presets:void 0,positionedMenuClass:"multi-select-container--positioned",
+positionMenuWithin:void 0,viewportBottomGutter:20,menuMinHeight:200};c.extend(f.prototype,{H:function(){this.v();this.G();this.A();this.w();this.B();this.J();this.K();this.b.hide()},v:function(){if(!1===this.b.is("select[multiple]"))throw Error("$.multiSelect only works on <select multiple> elements");},G:function(){this.l=c('label[for="'+this.b.attr("id")+'"]')},A:function(){this.f=c(this.a.containerHTML);this.b.data("multi-select-container",this.f);this.f.insertAfter(this.b)},w:function(){var b=
+this;this.g=c(this.a.buttonHTML);this.g.attr({role:"button","aria-haspopup":"true",tabindex:0,"aria-label":this.l.eq(0).text()}).on("keydown.multiselect",function(a){a=a.which;13!==a&&32!==a||b.g.click()}).on("click.multiselect",function(){b.m()}).appendTo(this.f);this.b.on("change.multiselect",function(){b.o()});this.o()},o:function(){var b=[],a=[];this.b.children("option").each(function(){var d=c(this).text();b.push(d);c(this).is(":selected")&&a.push(c.trim(d))});this.g.empty();0==a.length?this.g.text(this.a.noneText):
+a.length===b.length&&this.a.allText?this.g.text(this.a.allText):this.g.text(a.join(", "))},B:function(){var b=this;this.c=c(this.a.menuHTML);this.c.attr({role:"menu"}).on("keyup.multiselect",function(a){27===a.which&&b.j()}).appendTo(this.f);this.D();this.a.presets&&this.F()},D:function(){var b=this;this.h=c(this.a.menuItemsHTML);this.c.append(this.h);this.b.on("change.multiselect",function(a,c){!0!==c&&b.s()});this.s()},s:function(){var b=this;this.h.empty();this.b.children("option").each(function(a,
+d){a=b.C(c(d),a);b.h.append(a)})},F:function(){var b=this;this.i=c(this.a.presetsHTML);this.c.prepend(this.i);c.each(this.a.presets,function(a,d){a=b.b.attr("name")+"_preset_"+a;var h=c(b.a.menuItemHTML).attr({"for":a,role:"menuitem"}).text(" "+d.name).appendTo(b.i);c("<input>").attr({type:"radio",name:b.b.attr("name")+"_presets",id:a}).prependTo(h).on("change.multiselect",function(){b.b.val(d.options);b.b.trigger("change")})});this.b.on("change.multiselect",function(){b.u()});this.u()},u:function(){var b=
+this;c.each(this.a.presets,function(a,c){a=b.b.attr("name")+"_preset_"+a;a=b.i.find("#"+a);a:{c=c.options||[];var d=b.b.val()||[];if(c.length!=d.length)c=!1;else{c.sort();d.sort();for(var e=0;e<c.length;e++)if(c[e]!==d[e]){c=!1;break a}c=!0}}c?a.prop("checked",!0):a.prop("checked",!1)})},C:function(b,a){var d=this.b.attr("name")+"_"+a;a=c(this.a.menuItemHTML).attr({"for":d,role:"menuitem"}).text(" "+b.text());d=c("<input>").attr({type:"checkbox",id:d,value:b.val()}).prependTo(a);b.is(":disabled")&&
+d.attr("disabled","disabled");b.is(":selected")&&d.prop("checked","checked");d.on("change.multiselect",function(){c(this).prop("checked")?b.prop("selected",!0):b.prop("selected",!1);b.trigger("change",[!0])});return a},J:function(){var b=this;c("html").on("click.multiselect",function(){b.j()});this.f.on("click.multiselect",function(a){a.stopPropagation()})},K:function(){var b=this;this.l.on("click.multiselect",function(a){a.preventDefault();a.stopPropagation();b.m()})},I:function(){c("html").trigger("click.multiselect");
+this.f.addClass(this.a.activeClass);if(this.a.positionMenuWithin&&this.a.positionMenuWithin instanceof c){var b=this.c.offset().left+this.c.outerWidth(),a=this.a.positionMenuWithin.offset().left+this.a.positionMenuWithin.outerWidth();b>a&&(this.c.css("width",a-this.c.offset().left),this.f.addClass(this.a.positionedMenuClass))}b=this.c.offset().top+this.c.outerHeight();a=c(window).scrollTop()+c(window).height();b>a-this.a.viewportBottomGutter?this.c.css({maxHeight:Math.max(a-this.a.viewportBottomGutter-
+this.c.offset().top,this.a.menuMinHeight),overflow:"scroll"}):this.c.css({maxHeight:"",overflow:""})},j:function(){this.f.removeClass(this.a.activeClass);this.f.removeClass(this.a.positionedMenuClass);this.c.css("width","auto")},m:function(){this.f.hasClass(this.a.activeClass)?this.j():this.I()}});c.fn.multiSelect=function(b){return this.each(function(){c.data(this,"plugin_multiSelect")||c.data(this,"plugin_multiSelect",new f(this,b))})}})(jQuery);