Add action buttons to filter popups (#3882)
Adds action buttons ("Apply" & "Clear") to filter popups for UX improvement (past experience was click out of the popup to apply the selected filters and manually remove all applied filters). Credit to @Teranode. Fixes #3304.
This commit is contained in:
parent
5e877ea2ba
commit
8383f555ed
|
|
@ -84,14 +84,14 @@
|
|||
$scope.addClass('filter-scope-open')
|
||||
})
|
||||
|
||||
$(document).on('click', '#controlFilterPopover [data-trigger="filter"]', function (e) {
|
||||
$(document).on('click', '#controlFilterPopoverDate [data-trigger="filter"]', function (e) {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
|
||||
self.filterByDate()
|
||||
})
|
||||
|
||||
$(document).on('click', '#controlFilterPopover [data-trigger="clear"]', function (e) {
|
||||
$(document).on('click', '#controlFilterPopoverDate [data-trigger="clear"]', function (e) {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
|
||||
|
|
@ -106,7 +106,7 @@
|
|||
return ' \
|
||||
<form> \
|
||||
<input type="hidden" name="scopeName" value="{{ scopeName }}" /> \
|
||||
<div id="controlFilterPopover" class="control-filter-popover control-filter-box-popover"> \
|
||||
<div id="controlFilterPopoverDate" class="control-filter-popover control-filter-box-popover"> \
|
||||
<div class="filter-search loading-indicator-container size-input-text"> \
|
||||
<div class="field-datepicker"> \
|
||||
<div class="input-with-icon right-align"> \
|
||||
|
|
@ -135,46 +135,46 @@
|
|||
* Get popover range template
|
||||
*/
|
||||
FilterWidget.prototype.getPopoverRangeTemplate = function () {
|
||||
return ' \
|
||||
<form> \
|
||||
<input type="hidden" name="scopeName" value="{{ scopeName }}" /> \
|
||||
<div id="controlFilterPopover" class="control-filter-popover control-filter-box-popover --range"> \
|
||||
<div class="filter-search loading-indicator-container size-input-text"> \
|
||||
<div class="field-datepicker"> \
|
||||
<div class="input-with-icon right-align"> \
|
||||
<i class="icon icon-calendar-o"></i> \
|
||||
<input \
|
||||
type="text" \
|
||||
name="date" \
|
||||
value="{{ date }}" \
|
||||
class="form-control align-right popup-allow-focus" \
|
||||
autocomplete="off" \
|
||||
placeholder="{{ after_placeholder }}" /> \
|
||||
</div> \
|
||||
</div> \
|
||||
<div class="field-datepicker"> \
|
||||
<div class="input-with-icon right-align"> \
|
||||
<i class="icon icon-calendar-o"></i> \
|
||||
<input \
|
||||
type="text" \
|
||||
name="date" \
|
||||
value="{{ date }}" \
|
||||
class="form-control align-right" \
|
||||
autocomplete="off" \
|
||||
placeholder="{{ before_placeholder }}" /> \
|
||||
</div> \
|
||||
</div> \
|
||||
<div class="filter-buttons"> \
|
||||
<button class="btn btn-block btn-primary" data-trigger="filter"> \
|
||||
{{ filter_button_text }} \
|
||||
</button> \
|
||||
<button class="btn btn-block btn-secondary" data-trigger="clear"> \
|
||||
{{ reset_button_text }} \
|
||||
</button> \
|
||||
</div> \
|
||||
</div> \
|
||||
</div> \
|
||||
</form> \
|
||||
return ' \
|
||||
<form> \
|
||||
<input type="hidden" name="scopeName" value="{{ scopeName }}" /> \
|
||||
<div id="controlFilterPopoverDate" class="control-filter-popover control-filter-box-popover --range"> \
|
||||
<div class="filter-search loading-indicator-container size-input-text"> \
|
||||
<div class="field-datepicker"> \
|
||||
<div class="input-with-icon right-align"> \
|
||||
<i class="icon icon-calendar-o"></i> \
|
||||
<input \
|
||||
type="text" \
|
||||
name="date" \
|
||||
value="{{ date }}" \
|
||||
class="form-control align-right popup-allow-focus" \
|
||||
autocomplete="off" \
|
||||
placeholder="{{ after_placeholder }}" /> \
|
||||
</div> \
|
||||
</div> \
|
||||
<div class="field-datepicker"> \
|
||||
<div class="input-with-icon right-align"> \
|
||||
<i class="icon icon-calendar-o"></i> \
|
||||
<input \
|
||||
type="text" \
|
||||
name="date" \
|
||||
value="{{ date }}" \
|
||||
class="form-control align-right" \
|
||||
autocomplete="off" \
|
||||
placeholder="{{ before_placeholder }}" /> \
|
||||
</div> \
|
||||
</div> \
|
||||
<div class="filter-buttons"> \
|
||||
<button class="btn btn-block btn-primary" data-trigger="filter"> \
|
||||
{{ filter_button_text }} \
|
||||
</button> \
|
||||
<button class="btn btn-block btn-secondary" data-trigger="clear"> \
|
||||
{{ reset_button_text }} \
|
||||
</button> \
|
||||
</div> \
|
||||
</div> \
|
||||
</div> \
|
||||
</form> \
|
||||
'
|
||||
}
|
||||
|
||||
|
|
@ -238,7 +238,7 @@
|
|||
FilterWidget.prototype.initDatePickers = function (isRange) {
|
||||
var self = this,
|
||||
scopeData = this.$activeScope.data('scope-data'),
|
||||
$inputs = $('.field-datepicker input', '#controlFilterPopover'),
|
||||
$inputs = $('.field-datepicker input', '#controlFilterPopoverDate'),
|
||||
data = this.scopeValues[this.activeScopeName]
|
||||
|
||||
if (!data) {
|
||||
|
|
@ -277,7 +277,7 @@
|
|||
}
|
||||
|
||||
FilterWidget.prototype.clearDatePickers = function () {
|
||||
var $inputs = $('.field-datepicker input', '#controlFilterPopover')
|
||||
var $inputs = $('.field-datepicker input', '#controlFilterPopoverDate')
|
||||
|
||||
$inputs.each(function (index, datepicker) {
|
||||
var $datepicker = $(datepicker)
|
||||
|
|
@ -330,7 +330,7 @@
|
|||
dates = []
|
||||
|
||||
if (!isReset) {
|
||||
var datepickers = $('.field-datepicker input', '#controlFilterPopover')
|
||||
var datepickers = $('.field-datepicker input', '#controlFilterPopoverDate')
|
||||
|
||||
datepickers.each(function (index, datepicker) {
|
||||
var date = $(datepicker).data('pikaday').toString('YYYY-MM-DD')
|
||||
|
|
|
|||
|
|
@ -40,41 +40,49 @@
|
|||
* Get popover template
|
||||
*/
|
||||
FilterWidget.prototype.getPopoverTemplate = function() {
|
||||
return ' \
|
||||
<form> \
|
||||
<input type="hidden" name="scopeName" value="{{ scopeName }}" /> \
|
||||
<div id="controlFilterPopover" class="control-filter-popover"> \
|
||||
<div class="filter-search loading-indicator-container size-input-text"> \
|
||||
<button class="close" data-dismiss="popover" type="button">×</button> \
|
||||
<input \
|
||||
type="text" \
|
||||
name="search" \
|
||||
autocomplete="off" \
|
||||
class="filter-search-input form-control icon search popup-allow-focus" \
|
||||
data-request="{{ optionsHandler }}" \
|
||||
data-load-indicator-opaque \
|
||||
data-load-indicator \
|
||||
data-track-input /> \
|
||||
</div> \
|
||||
<div class="filter-items"> \
|
||||
<ul> \
|
||||
{{#available}} \
|
||||
<li data-item-id="{{id}}"><a href="javascript:;">{{name}}</a></li> \
|
||||
{{/available}} \
|
||||
{{#loading}} \
|
||||
<li class="loading"><span></span></li> \
|
||||
{{/loading}} \
|
||||
</ul> \
|
||||
</div> \
|
||||
<div class="filter-active-items"> \
|
||||
<ul> \
|
||||
{{#active}} \
|
||||
<li data-item-id="{{id}}"><a href="javascript:;">{{name}}</a></li> \
|
||||
{{/active}} \
|
||||
</ul> \
|
||||
</div> \
|
||||
</div> \
|
||||
</form> \
|
||||
return ' \
|
||||
<form> \
|
||||
<input type="hidden" name="scopeName" value="{{ scopeName }}" /> \
|
||||
<div id="controlFilterPopover" class="control-filter-popover control-filter-box-popover --range"> \
|
||||
<div class="filter-search loading-indicator-container size-input-text"> \
|
||||
<button class="close" data-dismiss="popover" type="button">×</button> \
|
||||
<input \
|
||||
type="text" \
|
||||
name="search" \
|
||||
autocomplete="off" \
|
||||
class="filter-search-input form-control icon search popup-allow-focus" \
|
||||
data-request="{{ optionsHandler }}" \
|
||||
data-load-indicator-opaque \
|
||||
data-load-indicator \
|
||||
data-track-input /> \
|
||||
<div class="filter-items"> \
|
||||
<ul> \
|
||||
{{#available}} \
|
||||
<li data-item-id="{{id}}"><a href="javascript:;">{{name}}</a></li> \
|
||||
{{/available}} \
|
||||
{{#loading}} \
|
||||
<li class="loading"><span></span></li> \
|
||||
{{/loading}} \
|
||||
</ul> \
|
||||
</div> \
|
||||
<div class="filter-active-items"> \
|
||||
<ul> \
|
||||
{{#active}} \
|
||||
<li data-item-id="{{id}}"><a href="javascript:;">{{name}}</a></li> \
|
||||
{{/active}} \
|
||||
</ul> \
|
||||
</div> \
|
||||
<div class="filter-buttons"> \
|
||||
<button class="btn btn-block btn-primary oc-icon-filter" data-trigger="apply"> \
|
||||
{{ apply_button_text }} \
|
||||
</button> \
|
||||
<button class="btn btn-block btn-secondary oc-icon-eraser" data-trigger="clear"> \
|
||||
{{ clear_button_text }} \
|
||||
</button> \
|
||||
</div> \
|
||||
</div> \
|
||||
</div> \
|
||||
</form> \
|
||||
'
|
||||
}
|
||||
|
||||
|
|
@ -137,6 +145,16 @@
|
|||
$(document).on('ajaxDone', '#controlFilterPopover input.filter-search-input', function(event, context, data){
|
||||
self.filterAvailable(data.scopeName, data.options.available)
|
||||
})
|
||||
|
||||
$(document).on('click', '#controlFilterPopover [data-trigger="apply"]', function (e) {
|
||||
e.preventDefault()
|
||||
self.filterScope()
|
||||
})
|
||||
|
||||
$(document).on('click', '#controlFilterPopover [data-trigger="clear"]', function (e) {
|
||||
e.preventDefault()
|
||||
self.filterScope(true)
|
||||
})
|
||||
}
|
||||
|
||||
FilterWidget.prototype.focusSearch = function() {
|
||||
|
|
@ -195,6 +213,7 @@
|
|||
if (item)
|
||||
toItems.push(item)
|
||||
|
||||
this.toggleFilterButtons(items)
|
||||
this.updateScopeSetting(this.$activeScope, items.active.length)
|
||||
this.isActiveScopeDirty = true
|
||||
this.focusSearch()
|
||||
|
|
@ -210,7 +229,12 @@
|
|||
data = { loading: true }
|
||||
isLoaded = false
|
||||
}
|
||||
|
||||
|
||||
data = $.extend({}, data, {
|
||||
apply_button_text: this.getLang('filter.scopes.apply_button_text', 'Apply'),
|
||||
clear_button_text: this.getLang('filter.scopes.clear_button_text', 'Clear')
|
||||
})
|
||||
|
||||
data.scopeName = scopeName
|
||||
data.optionsHandler = self.options.optionsHandler
|
||||
|
||||
|
|
@ -224,7 +248,9 @@
|
|||
closeOnPageClick: true,
|
||||
placement: 'bottom'
|
||||
})
|
||||
|
||||
|
||||
this.toggleFilterButtons()
|
||||
|
||||
// Load options for the first time
|
||||
if (!isLoaded) {
|
||||
self.loadOptions(scopeName)
|
||||
|
|
@ -256,6 +282,7 @@
|
|||
data: data,
|
||||
success: function(data) {
|
||||
self.fillOptions(scopeName, data.options)
|
||||
self.toggleFilterButtons()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -328,6 +355,18 @@
|
|||
})
|
||||
}
|
||||
|
||||
FilterWidget.prototype.toggleFilterButtons = function(data)
|
||||
{
|
||||
var items = $('#controlFilterPopover .filter-active-items > ul'),
|
||||
buttonContainer = $('#controlFilterPopover .filter-buttons')
|
||||
|
||||
if(data) {
|
||||
data.active.length > 0 ? buttonContainer.show() : buttonContainer.hide()
|
||||
} else {
|
||||
items.children().length > 0 ? buttonContainer.show() : buttonContainer.hide()
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Saves the options to the update handler
|
||||
*/
|
||||
|
|
@ -399,6 +438,19 @@
|
|||
$scope.toggleClass('active', !!switchValue)
|
||||
}
|
||||
|
||||
FilterWidget.prototype.filterScope = function (isReset) {
|
||||
var scopeName = this.$activeScope.data('scope-name')
|
||||
|
||||
if (isReset) {
|
||||
this.scopeValues[scopeName] = null
|
||||
this.updateScopeSetting(this.$activeScope, 0)
|
||||
}
|
||||
|
||||
this.pushOptions(scopeName);
|
||||
this.isActiveScopeDirty = true;
|
||||
this.$activeScope.data('oc.popover').hide()
|
||||
}
|
||||
|
||||
FilterWidget.prototype.getLang = function(name, defaultValue) {
|
||||
if ($.oc === undefined || $.oc.lang === undefined) {
|
||||
return defaultValue
|
||||
|
|
@ -407,7 +459,6 @@
|
|||
return $.oc.lang.get(name, defaultValue)
|
||||
}
|
||||
|
||||
|
||||
// FILTER WIDGET PLUGIN DEFINITION
|
||||
// ============================
|
||||
|
||||
|
|
|
|||
|
|
@ -63,6 +63,10 @@ return [
|
|||
'group' => [
|
||||
'all' => 'all'
|
||||
],
|
||||
'scopes' => [
|
||||
'apply_button_text' => 'Apply',
|
||||
'clear_button_text' => 'Clear'
|
||||
],
|
||||
'dates' => [
|
||||
'all' => 'all',
|
||||
'filter_button_text' => 'Filter',
|
||||
|
|
|
|||
Loading…
Reference in New Issue