Merge pull request #1651 from gabsource/feature/list-scope-filter-by-dates
Feature to allow filtering lists by date scopes (single date or range)
This commit is contained in:
commit
cdd280b6e1
|
|
@ -349,7 +349,11 @@ return [
|
|||
],
|
||||
'filter' => [
|
||||
'all' => 'all',
|
||||
'options_method_not_exists' => "The model class :model must define a method :method() returning options for the ':filter' filter."
|
||||
'options_method_not_exists' => "The model class :model must define a method :method() returning options for the ':filter' filter.",
|
||||
'date' => [
|
||||
'all' => 'all period',
|
||||
'format' =>'Y-m-d'
|
||||
]
|
||||
],
|
||||
'import_export' => [
|
||||
'upload_csv_file' => '1. Upload a CSV file',
|
||||
|
|
|
|||
|
|
@ -350,7 +350,11 @@ return [
|
|||
],
|
||||
'filter' => [
|
||||
'all' => 'tous',
|
||||
'options_method_not_exists' => "La classe du modèle :model doit définir une méthode :method() qui retourne les options pour le filtre ':filter'."
|
||||
'options_method_not_exists' => "La classe du modèle :model doit définir une méthode :method() qui retourne les options pour le filtre ':filter'.",
|
||||
'date' => [
|
||||
'all' => 'toute la période',
|
||||
'format' =>'d/m/Y'
|
||||
]
|
||||
],
|
||||
'import_export' => [
|
||||
'upload_csv_file' => '1. Envoyer un fichier CSV',
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
<?php namespace Backend\Widgets;
|
||||
|
||||
use Backend\Classes\FormField;
|
||||
use Backend\FormWidgets\DatePicker;
|
||||
use Carbon\Carbon;
|
||||
use Db;
|
||||
use Str;
|
||||
use Lang;
|
||||
|
|
@ -97,6 +100,14 @@ class Filter extends WidgetBase
|
|||
*/
|
||||
public function renderScopeElement($scope)
|
||||
{
|
||||
switch ($scope->type) {
|
||||
case 'datepicker':
|
||||
case 'daterangepicker':
|
||||
// Load datepicker assets
|
||||
new DatePicker($this->controller, new FormField('dummy', 'dummy'));
|
||||
break;
|
||||
}
|
||||
|
||||
return $this->makePartial('scope_'.$scope->type, ['scope' => $scope]);
|
||||
}
|
||||
|
||||
|
|
@ -133,6 +144,32 @@ class Filter extends WidgetBase
|
|||
$value = post('value');
|
||||
$this->setScopeValue($scope, $value);
|
||||
break;
|
||||
|
||||
case 'datepicker':
|
||||
$dates = $this->datesFromAjax(post('options.dates'));
|
||||
|
||||
if (!empty($dates)) {
|
||||
list($date) = $dates;
|
||||
} else {
|
||||
$date = null;
|
||||
}
|
||||
|
||||
$this->setScopeValue($scope, $date);
|
||||
break;
|
||||
|
||||
case 'daterangepicker':
|
||||
$dates = $this->datesFromAjax(post('options.dates'));
|
||||
|
||||
if (!empty($dates)) {
|
||||
list($after, $before) = $dates;
|
||||
|
||||
$dates = [$after, $before];
|
||||
} else {
|
||||
$dates = null;
|
||||
}
|
||||
|
||||
$this->setScopeValue($scope, $dates);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -392,6 +429,14 @@ class Filter extends WidgetBase
|
|||
$this->scopeModels[$name] = $model;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensure dates options are set
|
||||
*/
|
||||
if (!isset($config['minDate'])) {
|
||||
$scopeObj->minDate = '2000-01-01';
|
||||
$scopeObj->maxDate = '2099-12-31';
|
||||
}
|
||||
|
||||
$this->allScopes[$name] = $scopeObj;
|
||||
}
|
||||
}
|
||||
|
|
@ -451,38 +496,60 @@ class Filter extends WidgetBase
|
|||
return;
|
||||
}
|
||||
|
||||
$value = is_array($scope->value) ? array_keys($scope->value) : $scope->value;
|
||||
switch ($scope->type) {
|
||||
case 'datepicker':
|
||||
if ($scope->value instanceof Carbon && $scopeConditions = $scope->conditions) {
|
||||
$query->whereRaw(strtr($scopeConditions, [':filtered' => $scope->value->format('Y-m-d')]));
|
||||
}
|
||||
|
||||
/*
|
||||
* Condition
|
||||
*/
|
||||
if ($scopeConditions = $scope->conditions) {
|
||||
break;
|
||||
case 'daterangepicker':
|
||||
if (is_array($scope->value) && count($scope->value) > 1 && ($scopeConditions = $scope->conditions)) {
|
||||
list($after, $before) = array_values($scope->value);
|
||||
|
||||
/*
|
||||
* Switch scope: multiple conditions, value either 1 or 2
|
||||
*/
|
||||
if (is_array($scopeConditions)) {
|
||||
$conditionNum = is_array($value) ? 0 : $value - 1;
|
||||
list($scopeConditions) = array_slice($scopeConditions, $conditionNum);
|
||||
}
|
||||
if($after instanceof Carbon && $before instanceof Carbon) {
|
||||
$query->whereRaw(strtr($scopeConditions, [
|
||||
':after' => $after->format('Y-m-d'),
|
||||
':before' => $before->format('Y-m-d')
|
||||
]));
|
||||
}
|
||||
}
|
||||
|
||||
if (is_array($value)) {
|
||||
$filtered = implode(',', array_build($value, function ($key, $_value) {
|
||||
return [$key, Db::getPdo()->quote($_value)];
|
||||
}));
|
||||
}
|
||||
else {
|
||||
$filtered = Db::getPdo()->quote($value);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
$value = is_array($scope->value) ? array_keys($scope->value) : $scope->value;
|
||||
|
||||
$query->whereRaw(DbDongle::parse(strtr($scopeConditions, [':filtered' => $filtered])));
|
||||
}
|
||||
/*
|
||||
* Condition
|
||||
*/
|
||||
if ($scopeConditions = $scope->conditions) {
|
||||
|
||||
/*
|
||||
* Scope
|
||||
*/
|
||||
if ($scopeMethod = $scope->scope) {
|
||||
$query->$scopeMethod($value);
|
||||
/*
|
||||
* Switch scope: multiple conditions, value either 1 or 2
|
||||
*/
|
||||
if (is_array($scopeConditions)) {
|
||||
$conditionNum = is_array($value) ? 0 : $value - 1;
|
||||
list($scopeConditions) = array_slice($scopeConditions, $conditionNum);
|
||||
}
|
||||
|
||||
if (is_array($value)) {
|
||||
$filtered = implode(',', array_build($value, function ($key, $_value) {
|
||||
return [$key, Db::getPdo()->quote($_value)];
|
||||
}));
|
||||
}
|
||||
else {
|
||||
$filtered = Db::getPdo()->quote($value);
|
||||
}
|
||||
|
||||
$query->whereRaw(DbDongle::parse(strtr($scopeConditions, [':filtered' => $filtered])));
|
||||
}
|
||||
|
||||
/*
|
||||
* Scope
|
||||
*/
|
||||
if ($scopeMethod = $scope->scope) {
|
||||
$query->$scopeMethod($value);
|
||||
}
|
||||
}
|
||||
|
||||
return $query;
|
||||
|
|
@ -604,4 +671,45 @@ class Filter extends WidgetBase
|
|||
}
|
||||
return $processed;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert an array from the posted dates
|
||||
*
|
||||
* @param mixed $scope
|
||||
* @param array $dates
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function datesFromAjax($ajaxDates)
|
||||
{
|
||||
$dates = [];
|
||||
|
||||
if (null !== $ajaxDates) {
|
||||
if (!is_array($ajaxDates)) {
|
||||
$dates = [$ajaxDates];
|
||||
}
|
||||
|
||||
foreach ($ajaxDates as $date) {
|
||||
$dates[] = Carbon::createFromFormat('Y-m-d', $date);
|
||||
}
|
||||
}
|
||||
|
||||
return $dates;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param mixed $scope
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getFilterDateFormat($scope)
|
||||
{
|
||||
if (isset($scope->date_format)) {
|
||||
return $scope->date_format;
|
||||
}
|
||||
|
||||
return trans('backend::lang.filter.date.format');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
<!-- Group scope -->
|
||||
<a
|
||||
class="filter-scope-date <?= $scope->value ? 'active' : '' ?>"
|
||||
href="javascript:;"
|
||||
data-scope-name="<?= $scope->scopeName ?>"
|
||||
data-scope-data="<?= e(json_encode( [
|
||||
'date' => $scope->value && $scope->value instanceof \Carbon\Carbon ? $scope->value->format('Y-m-d') : null,
|
||||
'minDate' => $scope->minDate ? $scope->minDate : '2000-01-01' ,
|
||||
'maxDate' => $scope->maxDate ? $scope->maxDate : '2099-12-31',
|
||||
]))
|
||||
?>">
|
||||
<span class="filter-label"><?= e(trans($scope->label)) ?>:</span>
|
||||
<span class="filter-setting"><?= $scope->value && $scope->value instanceof \Carbon\Carbon ? $scope->value->format(trans('backend::lang.filter.date.format')) : e(trans('backend::lang.filter.date.all')) ?></span>
|
||||
</a>
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<!-- Group scope -->
|
||||
<a
|
||||
class="filter-scope-date range <?= $scope->value ? 'active' : '' ?>"
|
||||
href="javascript:;"
|
||||
data-scope-name="<?= $scope->scopeName ?>"
|
||||
data-scope-data="<?= e(json_encode( [
|
||||
'dates' => $scope->value && is_array($scope->value) ? array_map(function($value) { return $value instanceof \Carbon\Carbon ? $value->format('Y-m-d') : null; }, $scope->value) : null,
|
||||
'minDate' => $scope->minDate ? $scope->minDate : '2000-01-01' ,
|
||||
'maxDate' => $scope->maxDate ? $scope->maxDate : '2099-12-31',
|
||||
]))
|
||||
?>">
|
||||
<span class="filter-label"><?= e(trans($scope->label)) ?>:</span>
|
||||
<span class="filter-setting"><?= $scope->value && is_array($scope->value) ? join(' → ', array_map(function($value) { return $value instanceof \Carbon\Carbon ? $value->format(trans('backend::lang.filter.date.format')) : null; }, $scope->value)) : e(trans('backend::lang.filter.date.all')) ?></span>
|
||||
</a>
|
||||
|
|
@ -0,0 +1,344 @@
|
|||
/*
|
||||
* Filter Widget
|
||||
*
|
||||
* Data attributes:
|
||||
* - data-behavior="filter" - enables the filter plugin
|
||||
*
|
||||
* Dependences:
|
||||
* - October Popover (october.popover.js)
|
||||
*
|
||||
* Notes:
|
||||
* Ideally this control would not depend on loader or the AJAX framework,
|
||||
* then the Filter widget can use events to handle this business logic.
|
||||
*
|
||||
* Require:
|
||||
* - mustache/mustache
|
||||
* - modernizr/modernizr
|
||||
* - storm/popover
|
||||
*/
|
||||
+function ($) {
|
||||
"use strict";
|
||||
|
||||
var FilterWidget = $.fn.filterWidget.Constructor;
|
||||
|
||||
/* ------------------------------------------------------------------------
|
||||
* Overloaded module functions
|
||||
*/
|
||||
|
||||
var overloaded_init = FilterWidget.prototype.init;
|
||||
|
||||
FilterWidget.prototype.init = function () {
|
||||
overloaded_init.apply(this);
|
||||
|
||||
this.initFilterDate();
|
||||
};
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------
|
||||
* New module functions
|
||||
*/
|
||||
|
||||
FilterWidget.prototype.initFilterDate = function () {
|
||||
var self = this;
|
||||
|
||||
this.$el.on('show.oc.popover', 'a.filter-scope-date', function () {
|
||||
self.initDatePickers($(this).hasClass('range'));
|
||||
});
|
||||
|
||||
this.$el.on('hiding.oc.popover', 'a.filter-scope-date', function () {
|
||||
self.clearDatePickers();
|
||||
});
|
||||
|
||||
this.$el.on('hide.oc.popover', 'a.filter-scope-date', function () {
|
||||
var $scope = $(this);
|
||||
self.pushOptions(self.activeScopeName);
|
||||
self.activeScopeName = null;
|
||||
self.$activeScope = null;
|
||||
|
||||
// Second click closes the filter scope
|
||||
setTimeout(function () {
|
||||
$scope.removeClass('filter-scope-open')
|
||||
}, 200)
|
||||
});
|
||||
|
||||
this.$el.on('click', 'a.filter-scope-date', function () {
|
||||
var $scope = $(this),
|
||||
scopeName = $scope.data('scope-name');
|
||||
|
||||
// Ignore if already opened
|
||||
if ($scope.hasClass('filter-scope-open')) return;
|
||||
|
||||
// Ignore if another popover is opened
|
||||
if (null !== self.activeScopeName) return;
|
||||
|
||||
self.$activeScope = $scope;
|
||||
self.activeScopeName = scopeName;
|
||||
self.isActiveScopeDirty = false;
|
||||
|
||||
if ($scope.hasClass('range')) {
|
||||
self.displayPopoverRange($scope)
|
||||
} else {
|
||||
self.displayPopoverDate($scope)
|
||||
}
|
||||
|
||||
$scope.addClass('filter-scope-open')
|
||||
});
|
||||
|
||||
$(document).on('click', '#controlFilterPopover [data-trigger="filter"]', function (e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
self.filterByDate()
|
||||
});
|
||||
|
||||
$(document).on('click', '#controlFilterPopover [data-trigger="clear"]', function (e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
self.filterByDate(true)
|
||||
})
|
||||
};
|
||||
|
||||
/*
|
||||
* Get popover date template
|
||||
*/
|
||||
FilterWidget.prototype.getPopoverDateTemplate = function () {
|
||||
return ' \
|
||||
<form> \
|
||||
<input type="hidden" name="scopeName" value="{{ scopeName }}" /> \
|
||||
<div id="controlFilterPopover" class="control-filter-popover control-filter-date-popover"> \
|
||||
<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" \
|
||||
autocomplete="off" \
|
||||
placeholder="{{ date_placeholder }}" /> \
|
||||
</div> \
|
||||
</div> \
|
||||
<div class="filter-buttons"> \
|
||||
<button class="btn btn-block btn-default" data-trigger="clear"> \
|
||||
{{ reset_button_text }} \
|
||||
</button> \
|
||||
</div> \
|
||||
</div> \
|
||||
</div> \
|
||||
</form> \
|
||||
'
|
||||
};
|
||||
|
||||
/*
|
||||
* 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-date-popover"> \
|
||||
<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" \
|
||||
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 oc-icon-search" data-trigger="filter"> \
|
||||
{{ filter_button_text }} \
|
||||
</button> \
|
||||
<button class="btn btn-block btn-default" data-trigger="clear"> \
|
||||
{{ reset_button_text }} \
|
||||
</button> \
|
||||
</div> \
|
||||
</div> \
|
||||
</div> \
|
||||
</form> \
|
||||
'
|
||||
};
|
||||
|
||||
FilterWidget.prototype.displayPopoverDate = function ($scope) {
|
||||
var self = this,
|
||||
scopeName = $scope.data('scope-name'),
|
||||
data = this.scopeValues[scopeName];
|
||||
|
||||
data = $.extend({}, data, {
|
||||
filter_button_text: $.oc.lang.get('filter.dates.filter_button_text'),
|
||||
reset_button_text: $.oc.lang.get('filter.dates.reset_button_text'),
|
||||
date_placeholder: $.oc.lang.get('filter.dates.date_placeholder')
|
||||
});
|
||||
|
||||
data.scopeName = scopeName;
|
||||
|
||||
// Destroy any popovers already bound
|
||||
$scope.data('oc.popover', null);
|
||||
|
||||
$scope.ocPopover({
|
||||
content: Mustache.render(self.getPopoverDateTemplate(), data),
|
||||
modal: false,
|
||||
highlightModalTarget: true,
|
||||
closeOnPageClick: true,
|
||||
placement: 'bottom',
|
||||
onCheckDocumentClickTarget: function (target) {
|
||||
return self.onCheckDocumentClickTargetDatePicker(target);
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
FilterWidget.prototype.displayPopoverRange = function ($scope) {
|
||||
var self = this,
|
||||
scopeName = $scope.data('scope-name'),
|
||||
data = this.scopeValues[scopeName];
|
||||
|
||||
data = $.extend({}, data, {
|
||||
filter_button_text: $.oc.lang.get('filter.dates.filter_button_text'),
|
||||
reset_button_text: $.oc.lang.get('filter.dates.reset_button_text'),
|
||||
after_placeholder: $.oc.lang.get('filter.dates.after_placeholder'),
|
||||
before_placeholder: $.oc.lang.get('filter.dates.before_placeholder')
|
||||
});
|
||||
|
||||
data.scopeName = scopeName;
|
||||
|
||||
// Destroy any popovers already bound
|
||||
$scope.data('oc.popover', null);
|
||||
|
||||
$scope.ocPopover({
|
||||
content: Mustache.render(self.getPopoverRangeTemplate(), data),
|
||||
modal: false,
|
||||
highlightModalTarget: true,
|
||||
closeOnPageClick: true,
|
||||
placement: 'bottom',
|
||||
onCheckDocumentClickTarget: function (target) {
|
||||
return self.onCheckDocumentClickTargetDatePicker(target);
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
FilterWidget.prototype.initDatePickers = function (isRange) {
|
||||
var self = this,
|
||||
scopeData = self.$activeScope.data('scope-data'),
|
||||
$inputs = $('.field-datepicker input', '#controlFilterPopover'),
|
||||
data = self.scopeValues[self.activeScopeName];
|
||||
|
||||
if (!data) {
|
||||
data = {
|
||||
dates: isRange ? (scopeData.dates ? scopeData.dates : []) : (scopeData.date ? [scopeData.date] : [])
|
||||
}
|
||||
}
|
||||
|
||||
$inputs.each(function (index, datepicker) {
|
||||
var defaultValue = '',
|
||||
$datepicker = $(datepicker),
|
||||
defaults = {
|
||||
minDate: new Date(scopeData.minDate),
|
||||
maxDate: new Date(scopeData.maxDate),
|
||||
yearRange: 10,
|
||||
setDefaultDate: '' !== defaultValue ? defaultValue.toDate() : '',
|
||||
format: self.getDateFormat(),
|
||||
i18n: $.oc.lang.get('datepicker')
|
||||
};
|
||||
|
||||
if (0 <= index && index < data.dates.length) {
|
||||
defaultValue = moment(data.dates[index], 'YYYY-MM-DD')
|
||||
}
|
||||
|
||||
if (!isRange) {
|
||||
defaults.onSelect = function () {
|
||||
self.filterByDate()
|
||||
}
|
||||
}
|
||||
|
||||
datepicker.value = '' !== defaultValue ? defaultValue.format(self.getDateFormat()) : '';
|
||||
|
||||
$datepicker.pikaday(defaults)
|
||||
})
|
||||
};
|
||||
|
||||
FilterWidget.prototype.clearDatePickers = function () {
|
||||
var $inputs = $('.field-datepicker input', '#controlFilterPopover');
|
||||
|
||||
$inputs.each(function (index, datepicker) {
|
||||
var $datepicker = $(datepicker);
|
||||
|
||||
$datepicker.data('pikaday').destroy();
|
||||
})
|
||||
};
|
||||
|
||||
FilterWidget.prototype.updateScopeDateSetting = function ($scope, dates) {
|
||||
var self = this,
|
||||
$setting = $scope.find('.filter-setting'),
|
||||
dateFormat = self.getDateFormat();
|
||||
|
||||
if (dates && dates.length) {
|
||||
if (dates.length > 1) {
|
||||
var after = moment(dates[0], 'YYYY-MM-DD').format(dateFormat),
|
||||
before = moment(dates[1], 'YYYY-MM-DD').format(dateFormat);
|
||||
|
||||
$setting.text(after + ' → ' + before)
|
||||
} else {
|
||||
$setting.text(moment(dates[0], 'YYYY-MM-DD').format(dateFormat))
|
||||
}
|
||||
|
||||
$scope.addClass('active')
|
||||
}
|
||||
else {
|
||||
$setting.text($.oc.lang.get('filter.dates.all'));
|
||||
$scope.removeClass('active')
|
||||
}
|
||||
};
|
||||
|
||||
FilterWidget.prototype.filterByDate = function (isReset) {
|
||||
var self = this,
|
||||
dates = [];
|
||||
|
||||
if (!isReset) {
|
||||
$('.field-datepicker input', '#controlFilterPopover').each(function (index, datepicker) {
|
||||
dates.push($(datepicker).data('pikaday').toString('YYYY-MM-DD'))
|
||||
})
|
||||
}
|
||||
|
||||
self.updateScopeDateSetting(self.$activeScope, dates);
|
||||
self.scopeValues[self.activeScopeName] = {
|
||||
dates: dates
|
||||
};
|
||||
self.isActiveScopeDirty = true;
|
||||
self.$activeScope.data('oc.popover').hide()
|
||||
};
|
||||
|
||||
FilterWidget.prototype.getDateFormat = function () {
|
||||
return $.oc.lang.get('filter.dates.format')
|
||||
};
|
||||
|
||||
FilterWidget.prototype.onCheckDocumentClickTargetDatePicker = function (target) {
|
||||
var $target = $(target);
|
||||
|
||||
// If the click happens on a pikaday element, do not close the popover
|
||||
return $target.hasClass('pika-next') ||
|
||||
$target.hasClass('pika-prev') ||
|
||||
$target.hasClass('pika-select') ||
|
||||
$target.hasClass('pika-button') ||
|
||||
$target.parents('.pika-table').length ||
|
||||
$target.parents('.pika-title').length;
|
||||
};
|
||||
}(window.jQuery);
|
||||
|
|
@ -158,7 +158,7 @@
|
|||
$scope.addClass('active')
|
||||
}
|
||||
else {
|
||||
$setting.text('all')
|
||||
$setting.text($.oc.lang.get('filter.group.all'))
|
||||
$scope.removeClass('active')
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -100,6 +100,38 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .filter-scope-date {
|
||||
display: inline-block;
|
||||
padding: 15px;
|
||||
.filter-label {}
|
||||
.filter-setting {
|
||||
display: inline-block;
|
||||
.transition(color 0.6s);
|
||||
}
|
||||
|
||||
&:after {
|
||||
font-size: 14px;
|
||||
.icon(@angle-down);
|
||||
}
|
||||
|
||||
&.active {
|
||||
.filter-setting {
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
color: #FFF;
|
||||
background-color: @color-filter-bg-active;
|
||||
.border-radius(4px);
|
||||
.transition(~'color 1s, background-color 1s');
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: #000;
|
||||
.filter-label { color: @color-filter-text; }
|
||||
&.active .filter-setting { background-color: darken(@color-filter-bg-active, 5%); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.control-filter-popover {
|
||||
|
|
@ -183,6 +215,16 @@
|
|||
a:before { .icon(@times); }
|
||||
li.animate-enter { .animation(fadeInDown .5s); }
|
||||
}
|
||||
|
||||
&.control-filter-date-popover {
|
||||
min-width: 190px;
|
||||
|
||||
.filter-buttons .btn {
|
||||
border-radius: 0;
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: @screen-xs) {
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@
|
|||
=require js/tooltip.js
|
||||
=require js/toolbar.js
|
||||
=require js/filter.js
|
||||
=require js/filter.dates.js
|
||||
=require js/select.js
|
||||
=require js/loader.base.js
|
||||
=require js/loader.cursor.js
|
||||
|
|
|
|||
|
|
@ -59,6 +59,20 @@ return [
|
|||
'weekdaysShort' => ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
|
||||
],
|
||||
|
||||
'filter' => [
|
||||
'group' => [
|
||||
'all' => 'all'
|
||||
],
|
||||
'dates' => [
|
||||
'format' => 'YYYY-MM-DD', // Moment.js date format
|
||||
'all' => 'all period',
|
||||
'filter_button_text' => 'Filter',
|
||||
'reset_button_text' => 'Reset',
|
||||
'date_placeholder' => 'Date',
|
||||
'after_placeholder' => 'After',
|
||||
'before_placeholder' => 'Before'
|
||||
]
|
||||
],
|
||||
];
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -61,6 +61,21 @@ return [
|
|||
'weekdays' => ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi', 'Vendredi', 'Samedi'],
|
||||
'weekdaysShort' => ['Dim', 'Lun', 'Mar', 'Mer', 'Jeu', 'Ven', 'Sam']
|
||||
],
|
||||
|
||||
'filter' => [
|
||||
'group' => [
|
||||
'all' => 'tous'
|
||||
],
|
||||
'dates' => [
|
||||
'format' => 'DD/MM/YYYY', // Moment.js date format
|
||||
'all' => 'toute la période',
|
||||
'filter_button_text' => 'Filtrer',
|
||||
'reset_button_text' => 'Effacer',
|
||||
'date_placeholder' => 'Date',
|
||||
'after_placeholder' => 'Après le',
|
||||
'before_placeholder' => 'Avant le',
|
||||
]
|
||||
],
|
||||
];
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue