added table-column ordering (#1086)

This commit is contained in:
Kevin Papst
2019-09-09 23:47:42 +02:00
committed by GitHub
parent d041a3f4f9
commit a651e55dc9
82 changed files with 932 additions and 516 deletions

View File

@@ -29,6 +29,24 @@ export default class KimaiAjaxModalForm extends KimaiClickHandlerReducedInTableR
init() {
const self = this;
this.modal = jQuery('#remote_form_modal');
this.modal.on('hide.bs.modal', function () {
self.getContainer().getPlugin('event').trigger('modal-hide');
});
this.modal.on('hidden.bs.modal', function () {
// kill all references, so GC can kick in
self.getContainer().getPlugin('form').destroyForm(self._getFormIdentifier());
jQuery('#remote_form_modal .modal-body').replaceWith('');
});
this.modal.on('show.bs.modal', function () {
self.getContainer().getPlugin('event').trigger('modal-show');
});
this.modal.on('shown.bs.modal', function () {
// workaround for autofocus attribute, as the modal "steals" it
jQuery(self._getFormIdentifier()).find('input[type=text],textarea,select').filter(':not("[data-datetimepicker=on]")').filter(':visible:first').focus().delay(1000).focus();
});
this._addClickHandlerReducedInTableRow(this.selector, function(href) {
self.openUrlInModal(href);
});
@@ -57,17 +75,26 @@ export default class KimaiAjaxModalForm extends KimaiClickHandlerReducedInTableR
});
}
/**
* Returns the CSS selector for the modal form.
*
* @returns {string}
* @private
*/
_getFormIdentifier() {
return '#remote_form_modal .modal-content form';
}
_openFormInModal(html) {
const self = this;
// the modal that we use to render the form in
let formIdentifier = '#remote_form_modal .modal-content form';
let formIdentifier = this._getFormIdentifier();
// if any of these is found in a response, the form will be re-displayed
let flashErrorIdentifier = 'div.alert-error';
// messages to show above the form
let flashMessageIdentifier = 'div.alert';
let form = jQuery(formIdentifier);
let remoteModal = jQuery('#remote_form_modal');
let remoteModal = this.modal;
// will be (re-)activated later
form.off('submit');
@@ -90,11 +117,7 @@ export default class KimaiAjaxModalForm extends KimaiClickHandlerReducedInTableR
);
// activate new loaded widgets
self.getContainer().getPlugin('date-time-picker').activateDateTimePicker(formIdentifier);
self.getContainer().getPlugin('autocomplete').activateAutocomplete(formIdentifier + " .js-autocomplete");
// activate selectpicker if beta test is active
jQuery('.selectpicker').selectpicker('refresh');
self.getContainer().getPlugin('form').activateForm(formIdentifier);
}
// show error flash messages
@@ -113,11 +136,7 @@ export default class KimaiAjaxModalForm extends KimaiClickHandlerReducedInTableR
});
// -----------------------------------------------------------------------
// workaround for autofocus attribute, as the modal "steals" it
remoteModal.on('shown.bs.modal', function () {
jQuery(this).find('input[type=text],textarea,select').filter(':not("[data-datetimepicker=on]")').filter(':visible:first').focus().delay(1000).focus();
});
this.getContainer().getPlugin('toolbar').hide();
remoteModal.modal('show');
// the new form that was loaded via ajax

View File

@@ -21,7 +21,6 @@ export default class KimaiAutocomplete extends KimaiPlugin {
init() {
this.minChars = this.getContainer().getConfiguration().get('autoComplete');
this.activateAutocomplete(this.selector);
}
getId() {
@@ -36,60 +35,72 @@ export default class KimaiAutocomplete extends KimaiPlugin {
return this.splitTagList(term).pop();
}
activateAutocomplete(selector)
{
const apiUrl = jQuery(selector).attr('data-autocomplete-url');
activateAutocomplete(selector) {
const self = this;
const API = self.getContainer().getPlugin('api');
jQuery(selector + ' ' + this.selector).each(function(index) {
const currentField = jQuery(this);
const apiUrl = currentField.attr('data-autocomplete-url');
const API = self.getContainer().getPlugin('api');
jQuery(selector)
// don't navigate away from the field on tab when selecting an item
.on("keydown", function (event) {
if (event.keyCode === jQuery.ui.keyCode.TAB &&
jQuery(this).autocomplete("instance").menu.active) {
event.preventDefault();
}
})
.autocomplete({
source: function (request, response) {
const lastEntry = self.extractLastTag(request.term);
API.get(apiUrl, {'name': lastEntry}, function(data){
response(data);
});
},
search: function () {
// custom minLength
var term = self.extractLastTag(this.value);
if (term.length < self.minChars) {
return false;
currentField
// don't navigate away from the field on tab when selecting an item
.on("keydown", function (event) {
if (event.keyCode === jQuery.ui.keyCode.TAB &&
jQuery(this).autocomplete("instance").menu.active) {
event.preventDefault();
}
},
focus: function () {
// prevent value inserted on focus
return false;
},
select: function (event, ui) {
var terms = self.splitTagList(this.value);
})
.autocomplete({
source: function (request, response) {
const lastEntry = self.extractLastTag(request.term);
API.get(apiUrl, {'name': lastEntry}, function(data){
response(data);
});
},
search: function () {
// custom minLength
var term = self.extractLastTag(this.value);
if (term.length < self.minChars) {
return false;
}
},
focus: function () {
// prevent value inserted on focus
return false;
},
select: function (event, ui) {
var terms = self.splitTagList(this.value);
// remove the current input
terms.pop();
// remove the current input
terms.pop();
// check if selected tag is already in list
if (!terms.includes(ui.item.value)) {
// add the selected item
terms.push(ui.item.value);
// check if selected tag is already in list
if (!terms.includes(ui.item.value)) {
// add the selected item
terms.push(ui.item.value);
}
// add placeholder to get the comma-and-space at the end
terms.push("");
this.value = terms.join(", ");
$(this).trigger('change');
return false;
}
}
// add placeholder to get the comma-and-space at the end
terms.push("");
)
;
});
}
this.value = terms.join(", ");
$(this).trigger('change');
return false;
}
}
);
destroyAutocomplete(selector) {
jQuery(selector + ' ' + this.selector).each(function(index) {
const currentField = jQuery(this);
currentField.autocomplete("destroy");
currentField.removeData('autocomplete');
});
}
}

View File

@@ -21,12 +21,13 @@ export default class KimaiClickHandlerReducedInTableRow extends KimaiPlugin {
return;
}
// handles the "click" on table rows to open an entry for editing: when a button within a row is clicked,
// we don't want the table row event to be processed - so we intercept it
// handles the "click" on table rows to open an entry for editing
let target = event.target;
if (event.currentTarget.matches('tr')) {
while (target !== null && !target.matches('body')) {
if (target.matches('a') || target.matches ('button')) {
// when an element within the row is clicked, that can trigger stuff itself, we don't want the event to be processed
// don't act if a link, button or form element was clicked
if (target.matches('a') || target.matches ('button') || target.matches ('input')) {
return;
}
target = target.parentNode;

View File

@@ -14,9 +14,10 @@ import KimaiPlugin from "../KimaiPlugin";
export default class KimaiDatatable extends KimaiPlugin {
constructor(selector) {
constructor(contentAreaSelector, tableSelector) {
super();
this.selector = selector;
this.contentArea = contentAreaSelector;
this.selector = tableSelector;
}
getId() {
@@ -49,16 +50,18 @@ export default class KimaiDatatable extends KimaiPlugin {
document.addEventListener('toolbar-change', handle);
} else {
document.addEventListener('pagination-change', handle);
document.addEventListener('filter-change', handle);
}
}
reloadDatatable() {
const contentArea = this.contentArea;
const durations = this.getContainer().getPlugin('timesheet-duration');
const toolbarSelector = this.getContainer().getPlugin('toolbar').getSelector();
const form = jQuery(toolbarSelector);
let loading = '<div class="overlay"><i class="fas fa-sync fa-spin"></i></div>';
jQuery('section.content').append(loading);
jQuery(contentArea).append(loading);
// remove the empty fields to prevent errors
let formData = jQuery(toolbarSelector + ' :input')
@@ -72,8 +75,8 @@ export default class KimaiDatatable extends KimaiPlugin {
type: form.attr('method'),
data: formData,
success: function(html) {
jQuery('section.content').replaceWith(
jQuery(html).find('section.content')
jQuery(contentArea).replaceWith(
jQuery(html).find(contentArea)
);
durations.updateRecords();
},

View File

@@ -24,13 +24,9 @@ export default class KimaiDatePicker extends KimaiPlugin {
return 'date-picker';
}
init() {
this.activateDatePicker(this.selector);
}
activateDatePicker(selector) {
let translator = this.getContainer().getTranslation();
jQuery(selector + ' input[data-datepickerenable="on"]').each(function(index) {
const TRANSLATE = this.getContainer().getTranslation();
jQuery(selector + ' ' + this.selector).each(function(index) {
let localeFormat = jQuery(this).data('format');
jQuery(this).daterangepicker({
singleDatePicker: true,
@@ -39,9 +35,9 @@ export default class KimaiDatePicker extends KimaiPlugin {
locale: {
format: localeFormat,
firstDay: 1,
applyLabel: translator.get('confirm'),
cancelLabel: translator.get('cancel'),
customRangeLabel: translator.get('customRange'),
applyLabel: TRANSLATE.get('confirm'),
cancelLabel: TRANSLATE.get('cancel'),
customRangeLabel: TRANSLATE.get('customRange'),
daysOfWeek: moment.weekdaysShort(),
monthNames: moment.months(),
}
@@ -54,4 +50,13 @@ export default class KimaiDatePicker extends KimaiPlugin {
});
}
destroyDatePicker(selector) {
jQuery(selector + ' ' + this.selector).each(function(index) {
if (jQuery(this).data('daterangepicker') !== undefined) {
jQuery(this).daterangepicker('destroy');
jQuery(this).data('daterangepicker').remove();
}
});
}
}

View File

@@ -24,13 +24,9 @@ export default class KimaiDateRangePicker extends KimaiPlugin {
return 'date-range-picker';
}
init() {
this.activateDateRangePicker(this.selector);
}
activateDateRangePicker(selector) {
let translator = this.getContainer().getTranslation();
jQuery(selector + ' input[data-daterangepickerenable="on"]').each(function(index) {
jQuery(selector + ' ' + this.selector).each(function(index) {
let localeFormat = jQuery(this).data('format');
let separator = jQuery(this).data('separator');
let rangesList = {};
@@ -70,4 +66,13 @@ export default class KimaiDateRangePicker extends KimaiPlugin {
});
}
destroyDateRangePicker(selector) {
jQuery(selector + ' ' + this.selector).each(function(index) {
if (jQuery(this).data('daterangepicker') !== undefined) {
jQuery(this).daterangepicker('destroy');
jQuery(this).data('daterangepicker').remove();
}
});
}
}

View File

@@ -24,15 +24,11 @@ export default class KimaiDateTimePicker extends KimaiPlugin {
return 'date-time-picker';
}
init() {
this.activateDateTimePicker(this.selector);
}
activateDateTimePicker(selector) {
let translator = this.getContainer().getTranslation();
let configuration = this.getContainer().getConfiguration();
jQuery(selector + ' input[data-datetimepicker="on"]').each(function(index) {
jQuery(selector + ' ' + this.selector).each(function(index) {
let localeFormat = jQuery(this).data('format');
jQuery(this).daterangepicker({
singleDatePicker: true,
@@ -58,4 +54,13 @@ export default class KimaiDateTimePicker extends KimaiPlugin {
});
}
destroyDateTimePicker(selector) {
jQuery(selector + ' ' + this.selector).each(function(index) {
if (jQuery(this).data('daterangepicker') !== undefined) {
jQuery(this).daterangepicker('destroy');
jQuery(this).data('daterangepicker').remove();
}
});
}
}

View File

@@ -0,0 +1,35 @@
/*
* This file is part of the Kimai time-tracking app.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/*!
* [KIMAI] KimaiForm: basic functions for all forms
*/
import KimaiPlugin from "../KimaiPlugin";
export default class KimaiForm extends KimaiPlugin {
getId() {
return 'form';
}
activateForm(formSelector, container) {
this.getContainer().getPlugin('date-range-picker').activateDateRangePicker(formSelector);
this.getContainer().getPlugin('date-time-picker').activateDateTimePicker(formSelector);
this.getContainer().getPlugin('date-picker').activateDatePicker(formSelector);
this.getContainer().getPlugin('autocomplete').activateAutocomplete(formSelector);
this.getContainer().getPlugin('form-select').activateSelectPicker(formSelector, container);
}
destroyForm(formSelector) {
this.getContainer().getPlugin('form-select').destroySelectPicker(formSelector);
this.getContainer().getPlugin('autocomplete').destroyAutocomplete(formSelector);
this.getContainer().getPlugin('date-picker').destroyDatePicker(formSelector);
this.getContainer().getPlugin('date-time-picker').destroyDateTimePicker(formSelector);
this.getContainer().getPlugin('date-range-picker').destroyDateRangePicker(formSelector);
}
}

View File

@@ -0,0 +1,77 @@
/*
* This file is part of the Kimai time-tracking app.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/*!
* [KIMAI] KimaiFormSelect: enhanced functionality for HTML select's
*/
import KimaiPlugin from "../KimaiPlugin";
import jQuery from "jquery";
export default class KimaiFormSelect extends KimaiPlugin {
constructor(selector) {
super();
this.selector = selector;
}
getId() {
return 'form-select';
}
activateSelectPicker(selector, container) {
let options = {};
if (container !== undefined) {
options = {container: container};
}
jQuery(selector + ' ' + this.selector).selectpicker(options);
}
destroySelectPicker(selector) {
jQuery(selector + ' ' + this.selector).selectpicker('destroy');
}
updateOptions(selectIdentifier, data) {
let select = jQuery(selectIdentifier);
let emptyOption = jQuery(selectIdentifier + ' option[value=""]');
select.find('option').remove().end().find('optgroup').remove().end();
if (emptyOption.length !== 0) {
select.append('<option value="">' + emptyOption.text() + '</option>');
}
let htmlOptions = '';
let emptyOptions = '';
for (const [key, value] of Object.entries(data)) {
if (key === '__empty__') {
for (const entity of value) {
emptyOptions += '<option value="' + entity.id + '">' + entity.name + '</option>';
}
continue;
}
htmlOptions += '<optgroup label="' + key + '">';
for (const entity of value) {
htmlOptions += '<option value="' + entity.id + '">' + entity.name + '</option>';
}
htmlOptions += '</optgroup>';
}
select.append(htmlOptions);
select.append(emptyOptions);
// if we don't trigger the change, the other selects won't be resetted
select.trigger('change');
// if the beta test kimai.theme.select_type is active, this will tell the selects to refresh
if (select.hasClass('selectpicker')) {
select.selectpicker('refresh');
}
}
}

View File

@@ -1,28 +0,0 @@
/*
* This file is part of the Kimai time-tracking app.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/*!
* [KIMAI] KimaiJqueryPluginInitializer: initialize jQuery plugins
*/
import jQuery from 'jquery';
import KimaiPlugin from '../KimaiPlugin';
export default class KimaiJqueryPluginInitializer extends KimaiPlugin {
init() {
// activate the dropdown functionality
jQuery('.dropdown-toggle').dropdown();
// activate the tooltip functionality
jQuery('[data-toggle="tooltip"]').tooltip();
// enable all selectpicker in adhoc forms (like invoice and export)
$('.selectpicker').selectpicker({
container: 'body'
});
}
}

View File

@@ -1,49 +0,0 @@
/*
* This file is part of the Kimai time-tracking app.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/*!
* [KIMAI] KimaiSearchButtons: handles events of search buttons and the filter dropdown
*/
import jQuery from 'jquery';
import KimaiPlugin from "../KimaiPlugin";
/**
* FIXME refactor me and merge with KimaiToolbar
*/
export default class KimaiSearchButtons extends KimaiPlugin {
constructor(selector) {
super();
this.selector = selector;
}
init() {
const self = this;
$(document).on('click', this.selector + ' .search-toggle', function (e) {
e.stopPropagation();
jQuery(self.selector).toggleClass('search-open');
jQuery(self.selector + ' form.header-search').toggleClass('hidden-xs');
jQuery(self.selector + ' form.header-search .dropdown-toggle').dropdown('toggle');
jQuery(self.selector + ' form.header-search input#searchTerm').focus();
});
$(document).on('click', this.selector + ' .search-cancel', function (e) {
e.preventDefault();
jQuery(self.selector).toggleClass('search-open');
jQuery(self.selector + ' form.header-search .dropdown-toggle').dropdown('toggle');
jQuery(self.selector + ' form.header-search').toggleClass('hidden-xs');
});
// prevent that the dropdown closes, when a form input is changed - eg. a select option was clicked
$(document).on('click', this.selector + ' .dropdown-menu', function (e) {
e.stopPropagation();
});
}
}

View File

@@ -58,15 +58,6 @@ export default class KimaiSelectDataAPI extends KimaiPlugin {
}
_updateSelect(selectName, data) {
let select = jQuery(selectName);
let emptyOption = jQuery(selectName + ' option[value=""]');
select.find('option').remove().end().find('optgroup').remove().end();
if (emptyOption.length !== 0) {
select.append('<option value="">' + emptyOption.text() + '</option>');
}
const options = {};
for (const apiData of data) {
let title = apiData.parentTitle;
@@ -84,32 +75,7 @@ export default class KimaiSelectDataAPI extends KimaiPlugin {
ordered[key] = options[key];
});
let htmlOptions = '';
let emptyOptions = '';
for (const [key, value] of Object.entries(ordered)) {
if (key === '__empty__') {
for (const entity of value) {
emptyOptions += '<option value="' + entity.id + '">' + entity.name + '</option>';
}
continue;
}
htmlOptions += '<optgroup label="' + key + '">';
for (const entity of value) {
htmlOptions += '<option value="' + entity.id + '">' + entity.name + '</option>';
}
htmlOptions += '</optgroup>';
}
select.append(htmlOptions);
select.append(emptyOptions);
// if we don't trigger the change, the other selects won't be resetted
select.trigger('change');
// if the beta test kimai.theme.select_type is active, this will tell the selects to refresh
jQuery('.selectpicker').selectpicker('refresh');
this.getContainer().getPlugin('form-select').updateOptions(selectName, ordered);
}
}

View File

@@ -16,6 +16,13 @@ export default class KimaiThemeInitializer extends KimaiPlugin {
init() {
this.registerAutomaticAlertRemove('div.alert-success', 5000);
// activate the dropdown functionality
jQuery('.dropdown-toggle').dropdown();
// activate the tooltip functionality
jQuery('[data-toggle="tooltip"]').tooltip();
// activate all form plugins
this.getContainer().getPlugin('form').activateForm('.content-wrapper form', 'body');
}
/**

View File

@@ -14,9 +14,10 @@ import KimaiPlugin from "../KimaiPlugin";
export default class KimaiToolbar extends KimaiPlugin {
constructor(selector) {
constructor(formSelector, formSubmitActionClass) {
super();
this.selector = selector;
this.formSelector = formSelector;
this.actionClass = formSubmitActionClass;
}
getId() {
@@ -26,35 +27,71 @@ export default class KimaiToolbar extends KimaiPlugin {
init() {
const formSelector = this.getSelector();
const self = this;
const EVENT = self.getContainer().getPlugin('event');
// This catches all clicks on the pagination and prevents the default action, as we want to reload the page via JS
jQuery('body').on('click', 'div.navigation ul.pagination li a', function(event) {
let pager = jQuery(formSelector + " input#page");
if (pager.length === 0) {
return;
}
event.preventDefault();
event.stopPropagation();
let urlParts = jQuery(this).attr('href').split('/');
let page = urlParts[urlParts.length-1];
pager.val(page);
pager.trigger('change');
self.getContainer().getPlugin('event').trigger('pagination-change');
return false;
});
this._registerPagination(formSelector, EVENT);
this._registerSortableTables(formSelector, EVENT);
this._registerAlternativeSubmitActions(formSelector, this.actionClass);
this._registerSearchButtons(formSelector);
// Reset the page if any other value is changed, otherwise we might end up with a limited set
// of data which does not support the given page - and it would be just wrong to stay in the same page
jQuery(this.selector +' input').change(function (event) {
jQuery('body')
// prevent that the dropdown closes, when a form input is changed - eg. a select option was clicked
.on('click', formSelector + ' .dropdown-menu', function (event) {
const parent = jQuery(event.target).parents('.bootstrap-select');
if (parent.length === 0) {
event.stopPropagation();
jQuery(".bootstrap-select").removeClass("open");
}
})
// trying to emulate the normal behaviour fo the bootstrap-select, as using its default implementation
// leads to closing the surrounding dropdown menu
.on('click', formSelector + ' .bootstrap-select', function (event) {
const current = jQuery(this);
if (current.hasClass("open")){
jQuery(".bootstrap-select").removeClass("open");
} else {
jQuery(".bootstrap-select").not('.bs-container').each(function(index, element) {
var tmp = jQuery(element);
if (tmp.is(current)) {
return;
}
if (tmp.hasClass('open')) {
tmp.removeClass("open");
// the shown dropdown list will not be closed, using toggle hides all other lists BUT closes the containing search-dropdown
// tmp.find('select.selectpicker').selectpicker('toggle');
}
});
current.addClass("open");
}
event.stopPropagation();
})
// close bootstrap-select if a click happened outside (and none of the other clickHandler were called)
// if the click happened inside a bootstrap-select, we ignore this
.on('click', function(event) {
const parent = jQuery(event.target).parents('.bootstrap-select');
if (parent.length === 0) {
jQuery(".bootstrap-select").removeClass("open");
}
})
;
// Reset the page if filter values are changed, otherwise we might end up with a limited set of data,
// which does not support the given page - and it would be just wrong to stay in the same page
jQuery(formSelector +' input').change(function (event) {
switch (event.target.id) {
case 'order':
case 'orderBy':
case 'page':
break;
default:
jQuery(formSelector + ' input#page').val(1);
break;
}
self.triggerChange();
});
// when user selected a new customer or project, reset the pagination back to 1
// and then find out if the results should be reloaded
jQuery(formSelector + ' select').change(function (event) {
let reload = true;
switch (event.target.id) {
@@ -76,6 +113,119 @@ export default class KimaiToolbar extends KimaiPlugin {
self.triggerChange();
}
});
// close all open selectpicker upon choosing any dropdown option
jQuery(formSelector + ' select.selectpicker').on('change', function(event) {
jQuery('.bootstrap-select.open').removeClass('open');
});
}
/**
* The search toggle button is not part of this component, but it is directly connected to it.
* @private
*/
_registerSearchButtons(formSelector) {
jQuery('body')
// only for mobile experience currently: show the search form field
.on('click', '.btn-search.search-toggle', function (event) {
event.preventDefault();
event.stopPropagation();
jQuery(formSelector).parent('section').toggleClass('search-open');
jQuery(formSelector).toggleClass('hidden-xs');
jQuery(formSelector + ' input#searchTerm').dropdown('toggle');
jQuery(formSelector + ' input#searchTerm').focus();
})
// hide the search form field
.on('click', formSelector + ' a.search-cancel', function (event) {
event.preventDefault();
event.stopPropagation();
jQuery(formSelector).parent('section').toggleClass('search-open');
jQuery(formSelector + ' input#searchTerm').dropdown('toggle');
jQuery(formSelector).toggleClass('hidden-xs');
})
;
}
/**
* Some actions utilize the filter from the search form and submit it to another URL.
* @private
*/
_registerAlternativeSubmitActions(toolbarSelector, actionBtnClass) {
document.addEventListener('click', function(event) {
let target = event.target;
while (target !== null && !target.matches('body')) {
if (target.classList.contains(actionBtnClass)) {
const form = document.querySelector(toolbarSelector);
if (form === null) {
return;
}
const prevAction = form.action;
const prevMethod = form.method;
form.target = '_blank';
form.action = target.href;
if (target.dataset.method !== undefined) {
form.method = target.dataset.method;
}
form.submit();
form.target = '';
form.action = prevAction;
form.method = prevMethod;
event.preventDefault();
event.stopPropagation();
}
target = target.parentNode;
}
});
}
/**
* Sortable datatables use hidden fields in the toolbar filter/search form
* @private
*/
_registerSortableTables(formSelector, EVENT) {
jQuery('body').on('click', 'th.sortable', function(event){
var $header = jQuery(event.target);
var order = 'DESC';
var orderBy = $header.data('order');
if ($header.hasClass('sorting_desc')) {
order = 'ASC';
}
jQuery(formSelector + ' input#orderBy').val(orderBy);
jQuery(formSelector + ' input#order').val(order);
// triggers the page reset - see below
jQuery(formSelector + ' input#order').trigger('change');
// triggers the datatable reload - search for the event name
EVENT.trigger('filter-change');
});
}
/**
* This catches all clicks on the pagination and prevents the default action, as we want to reload the page via JS
* @private
*/
_registerPagination(formSelector, EVENT) {
jQuery('body').on('click', 'div.navigation ul.pagination li a', function(event) {
let pager = jQuery(formSelector + " input#page");
if (pager.length === 0) {
return;
}
event.preventDefault();
event.stopPropagation();
let urlParts = jQuery(this).attr('href').split('/');
let page = urlParts[urlParts.length-1];
pager.val(page);
pager.trigger('change');
EVENT.trigger('pagination-change');
return false;
});
}
hide() {
jQuery(this.getSelector() + ' .dropdown-toggle').dropdown('toggle');
}
/**
@@ -91,7 +241,7 @@ export default class KimaiToolbar extends KimaiPlugin {
* @returns {string}
*/
getSelector() {
return this.selector;
return this.formSelector;
}
}

View File

@@ -1,58 +0,0 @@
/*
* This file is part of the Kimai time-tracking app.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import KimaiPlugin from '../KimaiPlugin';
/**
* Needs to be initialized with a class name.
*
* A link like <a href=# class=remoteLink> can be activated with:
* new KimaiToolbarAction('remoteLink')
*
* @param selector
*/
export default class KimaiToolbarAction extends KimaiPlugin {
constructor(selector) {
super();
this.selector = selector;
}
init() {
const self = this;
const toolbarSelector = this.getContainer().getPlugin('toolbar').getSelector();
document.addEventListener('click', function(event) {
let target = event.target;
while (target !== null && !target.matches('body')) {
if (target.classList.contains(self.selector)) {
const form = document.querySelector(toolbarSelector);
if (form === null) {
return;
}
const prevAction = form.action;
const prevMethod = form.method;
form.target = '_blank';
form.action = target.href;
if (target.dataset.method !== undefined) {
form.method = target.dataset.method;
}
form.submit();
form.target = '';
form.action = prevAction;
form.method = prevMethod;
event.preventDefault();
event.stopPropagation();
}
target = target.parentNode;
}
});
}
}