fix(datatables): allow selecting range using shift (#344) (#2962)

* fix(datatables): allow selecting range using shift (#344)

* feat(datatables): more intuitive batch select behaviour

* feat(datatables): add overridable function called on selection change

* refactor(datatables): remove custom selectAll on Generic-extending Controllers

* fix(datatables): stored state data retrieval on Generic-extanding datatables controllers

* refactor(datatables): remove code duplication between GenericController and extending controllers
pull/2981/head
xAt0mZ 2019-07-02 17:51:17 +02:00 committed by GitHub
parent 6591498ab9
commit 1138fd5ab1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 477 additions and 582 deletions

View File

@ -50,7 +50,7 @@
<tr dir-paginate="item in ($ctrl.state.filteredDataSet = ($ctrl.dataset | filter:$ctrl.state.textFilter | orderBy:$ctrl.state.orderBy:$ctrl.state.reverseOrder | itemsPerPage: $ctrl.state.paginatedItemLimit))" ng-class="{active: item.Checked}">
<td>
<span class="md-checkbox">
<input id="select_{{ $index }}" type="checkbox" ng-model="item.Checked" ng-change="$ctrl.selectItem(item)"/>
<input id="select_{{ $index }}" type="checkbox" ng-model="item.Checked" ng-click="$ctrl.selectItem(item, $event)"/>
<label for="select_{{ $index }}"></label>
</span>
<a ui-sref="azure.containerinstances.container({ id: item.Id })">{{ item.Name | truncate:50 }}</a>

View File

@ -54,7 +54,7 @@
<tr dir-paginate="item in ($ctrl.state.filteredDataSet = ($ctrl.dataset | filter:$ctrl.state.textFilter | orderBy:$ctrl.state.orderBy:$ctrl.state.reverseOrder | itemsPerPage: $ctrl.state.paginatedItemLimit))" ng-class="{active: item.Checked}">
<td>
<span class="md-checkbox" authorization="DockerConfigDelete, DockerConfigCreate">
<input id="select_{{ $index }}" type="checkbox" ng-model="item.Checked" ng-change="$ctrl.selectItem(item)"/>
<input id="select_{{ $index }}" type="checkbox" ng-model="item.Checked" ng-click="$ctrl.selectItem(item, $event)"/>
<label for="select_{{ $index }}"></label>
</span>
<a ui-sref="docker.configs.config({id: item.Id})">{{ item.Name }}</a>

View File

@ -217,7 +217,7 @@
<tr dir-paginate="item in ($ctrl.state.filteredDataSet = ($ctrl.dataset | filter: $ctrl.applyFilters | filter:$ctrl.state.textFilter | orderBy:$ctrl.state.orderBy:$ctrl.state.reverseOrder | itemsPerPage: $ctrl.state.paginatedItemLimit))" ng-class="{active: item.Checked}">
<td>
<span class="md-checkbox" ng-if="!$ctrl.offlineMode" authorization="DockerContainerStart, DockerContainerStop, DockerContainerKill, DockerContainerRestart, DockerContainerPause, DockerContainerUnpause, DockerContainerDelete, DockerContainerCreate">
<input id="select_{{ $index }}" type="checkbox" ng-model="item.Checked" ng-change="$ctrl.selectItem(item)"/>
<input id="select_{{ $index }}" type="checkbox" ng-model="item.Checked" ng-click="$ctrl.selectItem(item, $event)"/>
<label for="select_{{ $index }}"></label>
</span>
<a ng-if="!$ctrl.offlineMode" ui-sref="docker.containers.container({ id: item.Id, nodeName: item.NodeName })" title="{{ item | containername }}">{{ item | containername | truncate: $ctrl.settings.containerNameTruncateSize }}</a>

View File

@ -1,22 +1,19 @@
import _ from 'lodash-es';
angular.module('portainer.docker')
.controller('ContainersDatatableController', ['PaginationService', 'DatatableService', 'EndpointProvider',
function (PaginationService, DatatableService, EndpointProvider) {
.controller('ContainersDatatableController', ['$scope', '$controller', 'DatatableService', 'EndpointProvider',
function ($scope, $controller, DatatableService, EndpointProvider) {
angular.extend(this, $controller('GenericDatatableController', {$scope: $scope}));
var ctrl = this;
this.state = {
selectAll: false,
orderBy: this.orderBy,
paginatedItemLimit: PaginationService.getPaginationLimit(this.tableKey),
displayTextFilter: false,
selectedItemCount: 0,
selectedItems: [],
this.state = Object.assign(this.state, {
noStoppedItemsSelected: true,
noRunningItemsSelected: true,
noPausedItemsSelected: true,
publicURL: EndpointProvider.endpointPublicURL()
};
});
this.settings = {
open: false,
@ -81,45 +78,13 @@ function (PaginationService, DatatableService, EndpointProvider) {
}
};
this.onTextFilterChange = function() {
DatatableService.setDataTableTextFilters(this.tableKey, this.state.textFilter);
};
this.onColumnVisibilityChange = function() {
DatatableService.setColumnVisibilitySettings(this.tableKey, this.columnVisibility);
};
this.changeOrderBy = function(orderField) {
this.state.reverseOrder = this.state.orderBy === orderField ? !this.state.reverseOrder : false;
this.state.orderBy = orderField;
DatatableService.setDataTableOrder(this.tableKey, orderField, this.state.reverseOrder);
};
this.toggleItemSelection = function(item) {
if (item.Checked) {
this.state.selectedItemCount++;
this.state.selectedItems.push(item);
} else {
this.state.selectedItems.splice(this.state.selectedItems.indexOf(item), 1);
this.state.selectedItemCount--;
}
};
this.selectItem = function(item) {
this.toggleItemSelection(item);
this.onSelectionChanged = function() {
this.updateSelectionState();
};
this.selectAll = function() {
for (var i = 0; i < this.state.filteredDataSet.length; i++) {
var item = this.state.filteredDataSet[i];
if (item.Checked !== this.state.selectAll) {
item.Checked = this.state.selectAll;
this.toggleItemSelection(item);
}
}
this.updateSelectionState();
};
}
this.updateSelectionState = function() {
this.state.noStoppedItemsSelected = true;
@ -144,10 +109,6 @@ function (PaginationService, DatatableService, EndpointProvider) {
}
};
this.changePaginationLimit = function() {
PaginationService.setPaginationLimit(this.tableKey, this.state.paginatedItemLimit);
};
this.applyFilters = function(value) {
var container = value;
var filters = ctrl.filters;
@ -209,40 +170,38 @@ function (PaginationService, DatatableService, EndpointProvider) {
};
this.$onInit = function() {
setDefaults(this);
this.setDefaults();
this.prepareTableFromDataset();
var storedOrder = DatatableService.getDataTableOrder(this.tableKey);
if (storedOrder !== null) {
this.state.reverseOrder = storedOrder.reverse;
this.state.orderBy = storedOrder.orderBy;
}
var textFilter = DatatableService.getDataTableTextFilters(this.tableKey);
if (textFilter !== null) {
this.state.textFilter = textFilter;
this.onTextFilterChange();
}
var storedFilters = DatatableService.getDataTableFilters(this.tableKey);
if (storedFilters !== null) {
this.filters = storedFilters;
this.filters.state.open = false;
this.updateStoredFilters(storedFilters.state.values);
}
this.filters.state.open = false;
var storedSettings = DatatableService.getDataTableSettings(this.tableKey);
if (storedSettings !== null) {
this.settings = storedSettings;
this.settings.open = false;
}
this.settings.open = false;
var storedColumnVisibility = DatatableService.getColumnVisibilitySettings(this.tableKey);
if (storedColumnVisibility !== null) {
this.columnVisibility = storedColumnVisibility;
}
this.columnVisibility.state.open = false;
var textFilter = DatatableService.getDataTableTextFilters(this.tableKey);
if (textFilter !== null) {
this.state.textFilter = textFilter;
this.columnVisibility.state.open = false;
}
};
function setDefaults(ctrl) {
ctrl.showTextFilter = ctrl.showTextFilter ? ctrl.showTextFilter : false;
ctrl.state.reverseOrder = ctrl.reverseOrder ? ctrl.reverseOrder : false;
}
}]);

View File

@ -1,15 +1,12 @@
import _ from 'lodash-es';
angular.module('portainer.docker')
.controller('JobsDatatableController', ['$q', '$state', 'PaginationService', 'DatatableService', 'ContainerService', 'ModalService', 'Notifications',
function ($q, $state, PaginationService, DatatableService, ContainerService, ModalService, Notifications) {
var ctrl = this;
.controller('JobsDatatableController', ['$scope', '$controller', '$q', '$state', 'PaginationService', 'DatatableService', 'ContainerService', 'ModalService', 'Notifications',
function ($scope, $controller, $q, $state, PaginationService, DatatableService, ContainerService, ModalService, Notifications) {
this.state = {
orderBy: this.orderBy,
paginatedItemLimit: PaginationService.getPaginationLimit(this.tableKey),
displayTextFilter: false
};
angular.extend(this, $controller('GenericDatatableController', {$scope: $scope}));
var ctrl = this;
this.filters = {
state: {
@ -19,20 +16,6 @@ angular.module('portainer.docker')
}
};
this.onTextFilterChange = function() {
DatatableService.setDataTableTextFilters(this.tableKey, this.state.textFilter);
};
this.changeOrderBy = function (orderField) {
this.state.reverseOrder = this.state.orderBy === orderField ? !this.state.reverseOrder : false;
this.state.orderBy = orderField;
DatatableService.setDataTableOrder(this.tableKey, orderField, this.state.reverseOrder);
};
this.changePaginationLimit = function () {
PaginationService.setPaginationLimit(this.tableKey, this.state.paginatedItemLimit);
};
this.applyFilters = function (value) {
var container = value;
var filters = ctrl.filters;
@ -121,8 +104,8 @@ angular.module('portainer.docker')
});
};
this.$onInit = function () {
setDefaults(this);
this.$onInit = function() {
this.setDefaults();
this.prepareTableFromDataset();
var storedOrder = DatatableService.getDataTableOrder(this.tableKey);
@ -131,21 +114,20 @@ angular.module('portainer.docker')
this.state.orderBy = storedOrder.orderBy;
}
var storedFilters = DatatableService.getDataTableFilters(this.tableKey);
if (storedFilters !== null) {
this.updateStoredFilters(storedFilters.state.values);
}
this.filters.state.open = false;
var textFilter = DatatableService.getDataTableTextFilters(this.tableKey);
if (textFilter !== null) {
this.state.textFilter = textFilter;
this.onTextFilterChange();
}
var storedFilters = DatatableService.getDataTableFilters(this.tableKey);
if (storedFilters !== null) {
this.filters = storedFilters;
this.updateStoredFilters(storedFilters.state.values);
}
if (this.filters && this.filters.state) {
this.filters.state.open = false;
}
};
function setDefaults(ctrl) {
ctrl.showTextFilter = ctrl.showTextFilter ? ctrl.showTextFilter : false;
ctrl.state.reverseOrder = ctrl.reverseOrder ? ctrl.reverseOrder : false;
}
}
]);

View File

@ -43,7 +43,7 @@
<table class="table table-hover table-filters nowrap-cells">
<thead>
<tr>
<th uib-dropdown dropdown-append-to-body auto-close="disabled" popover-placement="bottom-left" is-open="$ctrl.filters.usage.open">
<th uib-dropdown dropdown-append-to-body auto-close="disabled" popover-placement="bottom-left" is-open="$ctrl.filters.state.open">
<span class="md-checkbox" ng-if="!$ctrl.offlineMode">
<input id="select_all" type="checkbox" ng-model="$ctrl.state.selectAll" ng-change="$ctrl.selectAll()" />
<label for="select_all"></label>
@ -54,8 +54,8 @@
<i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Id' && $ctrl.state.reverseOrder"></i>
</a>
<div>
<span uib-dropdown-toggle class="table-filter" ng-if="!$ctrl.filters.usage.enabled">Filter <i class="fa fa-filter" aria-hidden="true"></i></span>
<span uib-dropdown-toggle class="table-filter filter-active" ng-if="$ctrl.filters.usage.enabled">Filter <i class="fa fa-check" aria-hidden="true"></i></span>
<span uib-dropdown-toggle class="table-filter" ng-if="!$ctrl.filters.state.enabled">Filter <i class="fa fa-filter" aria-hidden="true"></i></span>
<span uib-dropdown-toggle class="table-filter filter-active" ng-if="$ctrl.filters.state.enabled">Filter <i class="fa fa-check" aria-hidden="true"></i></span>
</div>
<div class="dropdown-menu" uib-dropdown-menu>
<div class="tableMenu">
@ -64,16 +64,16 @@
</div>
<div class="menuContent">
<div class="md-checkbox">
<input id="filter_usage_usedImages" type="checkbox" ng-model="$ctrl.filters.usage.showUsedImages" ng-change="$ctrl.onUsageFilterChange()"/>
<input id="filter_usage_usedImages" type="checkbox" ng-model="$ctrl.filters.state.showUsedImages" ng-change="$ctrl.onUsageFilterChange()"/>
<label for="filter_usage_usedImages">Used images</label>
</div>
<div class="md-checkbox">
<input id="filter_usage_unusedImages" type="checkbox" ng-model="$ctrl.filters.usage.showUnusedImages" ng-change="$ctrl.onUsageFilterChange()"/>
<input id="filter_usage_unusedImages" type="checkbox" ng-model="$ctrl.filters.state.showUnusedImages" ng-change="$ctrl.onUsageFilterChange()"/>
<label for="filter_usage_unusedImages">Unused images</label>
</div>
</div>
<div>
<a type="button" class="btn btn-default btn-sm" ng-click="$ctrl.filters.usage.open = false;">Close</a>
<a type="button" class="btn btn-default btn-sm" ng-click="$ctrl.filters.state.open = false;">Close</a>
</div>
</div>
</div>
@ -112,7 +112,7 @@
<tr dir-paginate="item in ($ctrl.state.filteredDataSet = ($ctrl.dataset | filter: $ctrl.applyFilters | filter:$ctrl.state.textFilter | orderBy:$ctrl.state.orderBy:$ctrl.state.reverseOrder | itemsPerPage: $ctrl.state.paginatedItemLimit))" ng-class="{active: item.Checked}">
<td>
<span class="md-checkbox" ng-if="!$ctrl.offlineMode">
<input id="select_{{ $index }}" type="checkbox" ng-model="item.Checked" ng-change="$ctrl.selectItem(item)"/>
<input id="select_{{ $index }}" type="checkbox" ng-model="item.Checked" ng-click="$ctrl.selectItem(item, $event)"/>
<label for="select_{{ $index }}"></label>
</span>
<a ng-if="!$ctrl.offlineMode" ui-sref="docker.images.image({ id: item.Id, nodeName: item.NodeName })" class="monospaced" title="{{ item.Id }}">{{ item.Id | truncate:40 }}</a>

View File

@ -1,20 +1,13 @@
angular.module('portainer.docker')
.controller('ImagesDatatableController', ['PaginationService', 'DatatableService',
function (PaginationService, DatatableService) {
.controller('ImagesDatatableController', ['$scope', '$controller', 'DatatableService',
function ($scope, $controller, DatatableService) {
angular.extend(this, $controller('GenericDatatableController', {$scope: $scope}));
var ctrl = this;
this.state = {
selectAll: false,
orderBy: this.orderBy,
paginatedItemLimit: PaginationService.getPaginationLimit(this.tableKey),
displayTextFilter: false,
selectedItemCount: 0,
selectedItems: []
};
this.filters = {
usage: {
state: {
open: false,
enabled: false,
showUsedImages: true,
@ -22,62 +15,29 @@ function (PaginationService, DatatableService) {
}
};
this.onTextFilterChange = function() {
DatatableService.setDataTableTextFilters(this.tableKey, this.state.textFilter);
};
this.changeOrderBy = function(orderField) {
this.state.reverseOrder = this.state.orderBy === orderField ? !this.state.reverseOrder : false;
this.state.orderBy = orderField;
DatatableService.setDataTableOrder(this.tableKey, orderField, this.state.reverseOrder);
};
this.selectItem = function(item) {
if (item.Checked) {
this.state.selectedItemCount++;
this.state.selectedItems.push(item);
} else {
this.state.selectedItems.splice(this.state.selectedItems.indexOf(item), 1);
this.state.selectedItemCount--;
}
};
this.selectAll = function() {
for (var i = 0; i < this.state.filteredDataSet.length; i++) {
var item = this.state.filteredDataSet[i];
if (item.Checked !== this.state.selectAll) {
item.Checked = this.state.selectAll;
this.selectItem(item);
}
}
};
this.changePaginationLimit = function() {
PaginationService.setPaginationLimit(this.tableKey, this.state.paginatedItemLimit);
};
this.applyFilters = function(value) {
var image = value;
var filters = ctrl.filters;
if ((image.ContainerCount === 0 && filters.usage.showUnusedImages)
|| (image.ContainerCount !== 0 && filters.usage.showUsedImages)) {
if ((image.ContainerCount === 0 && filters.state.showUnusedImages)
|| (image.ContainerCount !== 0 && filters.state.showUsedImages)) {
return true;
}
return false;
};
this.onUsageFilterChange = function() {
var filters = this.filters.usage;
this.onstateFilterChange = function() {
var filters = this.filters.state;
var filtered = false;
if (!filters.showUsedImages || !filters.showUnusedImages) {
filtered = true;
}
this.filters.usage.enabled = filtered;
this.filters.state.enabled = filtered;
DatatableService.setDataTableFilters(this.tableKey, this.filters);
};
this.$onInit = function() {
setDefaults(this);
this.setDefaults();
this.prepareTableFromDataset();
var storedOrder = DatatableService.getDataTableOrder(this.tableKey);
if (storedOrder !== null) {
@ -85,20 +45,18 @@ function (PaginationService, DatatableService) {
this.state.orderBy = storedOrder.orderBy;
}
var textFilter = DatatableService.getDataTableTextFilters(this.tableKey);
if (textFilter !== null) {
this.state.textFilter = textFilter;
this.onTextFilterChange();
}
var storedFilters = DatatableService.getDataTableFilters(this.tableKey);
if (storedFilters !== null) {
this.filters = storedFilters;
}
this.filters.usage.open = false;
var textFilter = DatatableService.getDataTableTextFilters(this.tableKey);
if (textFilter !== null) {
this.state.textFilter = textFilter;
if (this.filters && this.filters.state) {
this.filters.state.open = false;
}
};
function setDefaults(ctrl) {
ctrl.showTextFilter = ctrl.showTextFilter ? ctrl.showTextFilter : false;
ctrl.state.reverseOrder = ctrl.reverseOrder ? ctrl.reverseOrder : false;
}
}]);

View File

@ -60,7 +60,7 @@
ng-class="{active: item.Checked}">
<td>
<span class="md-checkbox">
<input id="select_{{ $index }}" type="checkbox" ng-model="item.Checked" ng-change="$ctrl.selectItem(item)" />
<input id="select_{{ $index }}" type="checkbox" ng-model="item.Checked" ng-click="$ctrl.selectItem(item, $event)" />
<label for="select_{{ $index }}"></label>
</span>
<a ui-sref="docker.nodes.node({id: item.Id})" ng-if="$ctrl.accessToNodeDetails">{{ item.Hostname }}</a>

View File

@ -110,7 +110,7 @@
<tr dir-paginate="item in ($ctrl.state.filteredDataSet = ($ctrl.dataset | filter:$ctrl.state.textFilter | orderBy:$ctrl.state.orderBy:$ctrl.state.reverseOrder | itemsPerPage: $ctrl.state.paginatedItemLimit))" ng-class="{active: item.Checked}">
<td>
<span class="md-checkbox" ng-if="!$ctrl.offlineMode">
<input id="select_{{ $index }}" type="checkbox" ng-model="item.Checked" ng-change="$ctrl.selectItem(item)" ng-disabled="$ctrl.disableRemove(item)"/>
<input id="select_{{ $index }}" type="checkbox" ng-model="item.Checked" ng-click="$ctrl.selectItem(item, $event)" ng-disabled="$ctrl.disableRemove(item)"/>
<label for="select_{{ $index }}"></label>
</span>
<a ng-if="!$ctrl.offlineMode" ui-sref="docker.networks.network({ id: item.Id, nodeName: item.NodeName })" title="{{ item.Name }}">{{ item.Name | truncate:40 }}</a>

View File

@ -1,19 +1,42 @@
angular.module('portainer.docker')
.controller('NetworksDatatableController', ['$scope', '$controller', 'PREDEFINED_NETWORKS',
function ($scope, $controller, PREDEFINED_NETWORKS) {
.controller('NetworksDatatableController', ['$scope', '$controller', 'PREDEFINED_NETWORKS', 'DatatableService',
function ($scope, $controller, PREDEFINED_NETWORKS, DatatableService) {
angular.extend(this, $controller('GenericDatatableController', {$scope: $scope}));
this.disableRemove = function(item) {
return PREDEFINED_NETWORKS.includes(item.Name);
};
this.selectAll = function() {
for (var i = 0; i < this.state.filteredDataSet.length; i++) {
var item = this.state.filteredDataSet[i];
if (!this.disableRemove(item) && item.Checked !== this.state.selectAll) {
item.Checked = this.state.selectAll;
this.selectItem(item);
}
/**
* Do not allow PREDEFINED_NETWORKS to be selected
*/
this.allowSelection = function(item) {
return !this.disableRemove(item);
}
this.$onInit = function() {
this.setDefaults();
this.prepareTableFromDataset();
var storedOrder = DatatableService.getDataTableOrder(this.tableKey);
if (storedOrder !== null) {
this.state.reverseOrder = storedOrder.reverse;
this.state.orderBy = storedOrder.orderBy;
}
var textFilter = DatatableService.getDataTableTextFilters(this.tableKey);
if (textFilter !== null) {
this.state.textFilter = textFilter;
this.onTextFilterChange();
}
var storedFilters = DatatableService.getDataTableFilters(this.tableKey);
if (storedFilters !== null) {
this.filters = storedFilters;
}
if (this.filters && this.filters.state) {
this.filters.state.open = false;
}
};
}

View File

@ -54,7 +54,7 @@
<tr dir-paginate="item in ($ctrl.state.filteredDataSet = ($ctrl.dataset | filter:$ctrl.state.textFilter | orderBy:$ctrl.state.orderBy:$ctrl.state.reverseOrder | itemsPerPage: $ctrl.state.paginatedItemLimit))" ng-class="{active: item.Checked}">
<td>
<span class="md-checkbox" authorization="DockerSecretDelete, DockerSecretCreate">
<input id="select_{{ $index }}" type="checkbox" ng-model="item.Checked" ng-change="$ctrl.selectItem(item)"/>
<input id="select_{{ $index }}" type="checkbox" ng-model="item.Checked" ng-click="$ctrl.selectItem(item, $event)"/>
<label for="select_{{ $index }}"></label>
</span>
<a ui-sref="docker.secrets.secret({id: item.Id})">{{ item.Name }}</a>

View File

@ -1,77 +1,84 @@
import _ from 'lodash-es';
angular.module('portainer.docker')
.controller('ServiceTasksDatatableController', ['DatatableService',
function (DatatableService) {
var ctrl = this;
.controller('ServiceTasksDatatableController', ['$scope', '$controller', 'DatatableService',
function ($scope, $controller, DatatableService) {
this.state = {
orderBy: this.orderBy,
showQuickActionStats: true,
showQuickActionLogs: true,
showQuickActionExec: true,
showQuickActionInspect: true,
showQuickActionAttach: false
};
angular.extend(this, $controller('GenericDatatableController', {$scope: $scope}));
this.filters = {
state: {
open: false,
enabled: false,
values: []
var ctrl = this;
this.state = Object.assign(this.state, {
showQuickActionStats: true,
showQuickActionLogs: true,
showQuickActionConsole: true,
showQuickActionInspect: true,
showQuickActionAttach: false
});
this.filters = {
state: {
open: false,
enabled: false,
values: []
}
};
this.applyFilters = function(item) {
var filters = ctrl.filters;
for (var i = 0; i < filters.state.values.length; i++) {
var filter = filters.state.values[i];
if (item.Status.State === filter.label && filter.display) {
return true;
}
}
return false;
};
this.onStateFilterChange = function() {
var filters = this.filters.state.values;
var filtered = false;
for (var i = 0; i < filters.length; i++) {
var filter = filters[i];
if (!filter.display) {
filtered = true;
}
}
this.filters.state.enabled = filtered;
};
this.prepareTableFromDataset = function() {
var availableStateFilters = [];
for (var i = 0; i < this.dataset.length; i++) {
var item = this.dataset[i];
availableStateFilters.push({ label: item.Status.State, display: true });
}
this.filters.state.values = _.uniqBy(availableStateFilters, 'label');
};
this.$onInit = function() {
this.setDefaults();
this.prepareTableFromDataset();
var storedOrder = DatatableService.getDataTableOrder(this.tableKey);
if (storedOrder !== null) {
this.state.reverseOrder = storedOrder.reverse;
this.state.orderBy = storedOrder.orderBy;
}
var textFilter = DatatableService.getDataTableTextFilters(this.tableKey);
if (textFilter !== null) {
this.state.textFilter = textFilter;
this.onTextFilterChange();
}
var storedFilters = DatatableService.getDataTableFilters(this.tableKey);
if (storedFilters !== null) {
this.filters = storedFilters;
}
if (this.filters && this.filters.state) {
this.filters.state.open = false;
}
};
}
};
this.applyFilters = function(item) {
var filters = ctrl.filters;
for (var i = 0; i < filters.state.values.length; i++) {
var filter = filters.state.values[i];
if (item.Status.State === filter.label && filter.display) {
return true;
}
}
return false;
};
this.onStateFilterChange = function() {
var filters = this.filters.state.values;
var filtered = false;
for (var i = 0; i < filters.length; i++) {
var filter = filters[i];
if (!filter.display) {
filtered = true;
}
}
this.filters.state.enabled = filtered;
};
this.changeOrderBy = function(orderField) {
this.state.reverseOrder = this.state.orderBy === orderField ? !this.state.reverseOrder : false;
this.state.orderBy = orderField;
DatatableService.setDataTableOrder(this.tableKey, orderField, this.state.reverseOrder);
};
this.prepareTableFromDataset = function() {
var availableStateFilters = [];
for (var i = 0; i < this.dataset.length; i++) {
var item = this.dataset[i];
availableStateFilters.push({ label: item.Status.State, display: true });
}
this.filters.state.values = _.uniqBy(availableStateFilters, 'label');
};
this.$onInit = function() {
setDefaults(this);
this.prepareTableFromDataset();
var storedOrder = DatatableService.getDataTableOrder(this.tableKey);
if (storedOrder !== null) {
this.state.reverseOrder = storedOrder.reverse;
this.state.orderBy = storedOrder.orderBy;
}
};
function setDefaults(ctrl) {
ctrl.state.reverseOrder = ctrl.reverseOrder ? ctrl.reverseOrder : false;
}
}]);
]);

View File

@ -84,7 +84,7 @@
<tr ng-click="$ctrl.expandItem(item, !item.Expanded)" dir-paginate-start="item in ($ctrl.state.filteredDataSet = ($ctrl.dataset | filter:$ctrl.state.textFilter | orderBy:$ctrl.state.orderBy:$ctrl.state.reverseOrder | itemsPerPage: $ctrl.state.paginatedItemLimit))" ng-class="{active: item.Checked}" class="interactive">
<td>
<span class="md-checkbox" authorization="DockerServiceUpdate, DockerServiceDelete, DockerServiceCreate">
<input id="select_{{ $index }}" type="checkbox" ng-model="item.Checked" ng-change="$ctrl.selectItem(item)"/>
<input id="select_{{ $index }}" type="checkbox" ng-model="item.Checked" ng-click="$ctrl.selectItem(item, $event)"/>
<label for="select_{{ $index }}"></label>
</span>
<a><i ng-class="{ 'fas fa-angle-down': item.Expanded, 'fas fa-angle-right': !item.Expanded }" class="space-right" aria-hidden="true"></i></a>

View File

@ -1,52 +1,18 @@
import _ from 'lodash-es';
angular.module('portainer.docker')
.controller('ServicesDatatableController', ['PaginationService', 'DatatableService', 'EndpointProvider',
function (PaginationService, DatatableService, EndpointProvider) {
.controller('ServicesDatatableController', ['$scope', '$controller', 'DatatableService', 'EndpointProvider',
function ($scope, $controller, DatatableService, EndpointProvider) {
angular.extend(this, $controller('GenericDatatableController', {$scope: $scope}));
var ctrl = this;
this.state = {
selectAll: false,
this.state = Object.assign(this.state,{
expandAll: false,
orderBy: this.orderBy,
paginatedItemLimit: PaginationService.getPaginationLimit(this.tableKey),
displayTextFilter: false,
selectedItemCount: 0,
selectedItems: [],
expandedItems: [],
publicURL: EndpointProvider.endpointPublicURL()
};
this.onTextFilterChange = function() {
DatatableService.setDataTableTextFilters(this.tableKey, this.state.textFilter);
};
this.changeOrderBy = function(orderField) {
this.state.reverseOrder = this.state.orderBy === orderField ? !this.state.reverseOrder : false;
this.state.orderBy = orderField;
DatatableService.setDataTableOrder(this.tableKey, orderField, this.state.reverseOrder);
};
this.selectItem = function(item) {
if (item.Checked) {
this.state.selectedItemCount++;
this.state.selectedItems.push(item);
} else {
this.state.selectedItems.splice(this.state.selectedItems.indexOf(item), 1);
this.state.selectedItemCount--;
}
};
this.selectAll = function() {
for (var i = 0; i < this.state.filteredDataSet.length; i++) {
var item = this.state.filteredDataSet[i];
if (item.Checked !== this.state.selectAll) {
item.Checked = this.state.selectAll;
this.selectItem(item);
}
}
};
});
this.expandAll = function() {
this.state.expandAll = !this.state.expandAll;
@ -56,10 +22,6 @@ function (PaginationService, DatatableService, EndpointProvider) {
}
};
this.changePaginationLimit = function() {
PaginationService.setPaginationLimit(this.tableKey, this.state.paginatedItemLimit);
};
this.expandItem = function(item, expanded) {
item.Expanded = expanded;
if (item.Expanded) {
@ -103,7 +65,8 @@ function (PaginationService, DatatableService, EndpointProvider) {
};
this.$onInit = function() {
setDefaults(this);
this.setDefaults();
this.prepareTableFromDataset();
var storedOrder = DatatableService.getDataTableOrder(this.tableKey);
if (storedOrder !== null) {
@ -111,19 +74,23 @@ function (PaginationService, DatatableService, EndpointProvider) {
this.state.orderBy = storedOrder.orderBy;
}
var textFilter = DatatableService.getDataTableTextFilters(this.tableKey);
if (textFilter !== null) {
this.state.textFilter = textFilter;
this.onTextFilterChange();
}
var storedFilters = DatatableService.getDataTableFilters(this.tableKey);
if (storedFilters !== null) {
this.filters = storedFilters;
}
if (this.filters && this.filters.state) {
this.filters.state.open = false;
}
var storedExpandedItems = DatatableService.getDataTableExpandedItems(this.tableKey);
if (storedExpandedItems !== null) {
this.expandItems(storedExpandedItems);
}
var textFilter = DatatableService.getDataTableTextFilters(this.tableKey);
if (textFilter !== null) {
this.state.textFilter = textFilter;
}
};
function setDefaults(ctrl) {
ctrl.showTextFilter = ctrl.showTextFilter ? ctrl.showTextFilter : false;
ctrl.state.reverseOrder = ctrl.reverseOrder ? ctrl.reverseOrder : false;
}
}]);

View File

@ -1,56 +1,20 @@
angular.module('portainer.docker')
.controller('TasksDatatableController', ['PaginationService', 'DatatableService',
function (PaginationService, DatatableService) {
this.state = {
.controller('TasksDatatableController', ['$scope', '$controller', 'DatatableService',
function ($scope, $controller, DatatableService) {
angular.extend(this, $controller('GenericDatatableController', {$scope: $scope}));
this.state = Object.assign(this.state, {
showQuickActionStats: true,
showQuickActionLogs: true,
showQuickActionExec: true,
showQuickActionInspect: true,
showQuickActionAttach: false,
selectAll: false,
orderBy: this.orderBy,
paginatedItemLimit: PaginationService.getPaginationLimit(this.tableKey),
displayTextFilter: false,
selectedItemCount: 0,
selectedItems: []
};
this.onTextFilterChange = function() {
DatatableService.setDataTableTextFilters(this.tableKey, this.state.textFilter);
};
this.changeOrderBy = function(orderField) {
this.state.reverseOrder = this.state.orderBy === orderField ? !this.state.reverseOrder : false;
this.state.orderBy = orderField;
DatatableService.setDataTableOrder(this.tableKey, orderField, this.state.reverseOrder);
};
this.selectItem = function(item) {
if (item.Checked) {
this.state.selectedItemCount++;
this.state.selectedItems.push(item);
} else {
this.state.selectedItems.splice(this.state.selectedItems.indexOf(item), 1);
this.state.selectedItemCount--;
}
};
this.selectAll = function() {
for (var i = 0; i < this.state.filteredDataSet.length; i++) {
var item = this.state.filteredDataSet[i];
if (item.Checked !== this.state.selectAll) {
item.Checked = this.state.selectAll;
this.selectItem(item);
}
}
};
this.changePaginationLimit = function() {
PaginationService.setPaginationLimit(this.tableKey, this.state.paginatedItemLimit);
};
showQuickActionAttach: false
});
this.$onInit = function() {
setDefaults(this);
this.setDefaults();
this.prepareTableFromDataset();
var storedOrder = DatatableService.getDataTableOrder(this.tableKey);
if (storedOrder !== null) {
@ -61,11 +25,15 @@ function (PaginationService, DatatableService) {
var textFilter = DatatableService.getDataTableTextFilters(this.tableKey);
if (textFilter !== null) {
this.state.textFilter = textFilter;
this.onTextFilterChange();
}
var storedFilters = DatatableService.getDataTableFilters(this.tableKey);
if (storedFilters !== null) {
this.filters = storedFilters;
}
if (this.filters && this.filters.state) {
this.filters.state.open = false;
}
};
function setDefaults(ctrl) {
ctrl.showTextFilter = ctrl.showTextFilter ? ctrl.showTextFilter : false;
ctrl.state.reverseOrder = ctrl.reverseOrder ? ctrl.reverseOrder : false;
}
}]);

View File

@ -23,7 +23,7 @@
<table class="table table-hover table-filters nowrap-cells">
<thead>
<tr>
<th uib-dropdown dropdown-append-to-body auto-close="disabled" is-open="$ctrl.filters.usage.open">
<th uib-dropdown dropdown-append-to-body auto-close="disabled" is-open="$ctrl.filters.state.open">
<span class="md-checkbox" ng-if="!$ctrl.offlineMode" authorization="DockerVolumeDelete, DockerVolumeCreate">
<input id="select_all" type="checkbox" ng-model="$ctrl.state.selectAll" ng-change="$ctrl.selectAll()" />
<label for="select_all"></label>
@ -34,8 +34,8 @@
<i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Id' && $ctrl.state.reverseOrder"></i>
</a>
<div>
<span uib-dropdown-toggle class="table-filter" ng-if="!$ctrl.filters.usage.enabled">Filter <i class="fa fa-filter" aria-hidden="true"></i></span>
<span uib-dropdown-toggle class="table-filter filter-active" ng-if="$ctrl.filters.usage.enabled">Filter <i class="fa fa-check" aria-hidden="true"></i></span>
<span uib-dropdown-toggle class="table-filter" ng-if="!$ctrl.filters.state.enabled">Filter <i class="fa fa-filter" aria-hidden="true"></i></span>
<span uib-dropdown-toggle class="table-filter filter-active" ng-if="$ctrl.filters.state.enabled">Filter <i class="fa fa-check" aria-hidden="true"></i></span>
</div>
<div class="dropdown-menu" uib-dropdown-menu>
<div class="tableMenu">
@ -44,16 +44,16 @@
</div>
<div class="menuContent">
<div class="md-checkbox">
<input id="filter_usage_usedImages" type="checkbox" ng-model="$ctrl.filters.usage.showUsedVolumes" ng-change="$ctrl.onUsageFilterChange()"/>
<input id="filter_usage_usedImages" type="checkbox" ng-model="$ctrl.filters.state.showUsedVolumes" ng-change="$ctrl.onUsageFilterChange()"/>
<label for="filter_usage_usedImages">Used volumes</label>
</div>
<div class="md-checkbox">
<input id="filter_usage_unusedImages" type="checkbox" ng-model="$ctrl.filters.usage.showUnusedVolumes" ng-change="$ctrl.onUsageFilterChange()"/>
<input id="filter_usage_unusedImages" type="checkbox" ng-model="$ctrl.filters.state.showUnusedVolumes" ng-change="$ctrl.onUsageFilterChange()"/>
<label for="filter_usage_unusedImages">Unused volumes</label>
</div>
</div>
<div>
<a type="button" class="btn btn-default btn-sm" ng-click="$ctrl.filters.usage.open = false;">Close</a>
<a type="button" class="btn btn-default btn-sm" ng-click="$ctrl.filters.state.open = false;">Close</a>
</div>
</div>
</div>
@ -106,7 +106,7 @@
<tr dir-paginate="item in ($ctrl.state.filteredDataSet = ($ctrl.dataset | filter: $ctrl.applyFilters | filter:$ctrl.state.textFilter | orderBy:$ctrl.state.orderBy:$ctrl.state.reverseOrder | itemsPerPage: $ctrl.state.paginatedItemLimit))" ng-class="{active: item.Checked}">
<td>
<span class="md-checkbox" ng-if="!$ctrl.offlineMode" authorization="DockerVolumeDelete, DockerVolumeCreate">
<input id="select_{{ $index }}" type="checkbox" ng-model="item.Checked" ng-change="$ctrl.selectItem(item)"/>
<input id="select_{{ $index }}" type="checkbox" ng-model="item.Checked" ng-click="$ctrl.selectItem(item, $event)"/>
<label for="select_{{ $index }}"></label>
</span>
<a ng-if="!$ctrl.offlineMode" ui-sref="docker.volumes.volume({ id: item.Id, nodeName: item.NodeName })" class="monospaced" title="{{ item.Id }}">{{ item.Id | truncate:40 }}</a>

View File

@ -1,20 +1,13 @@
angular.module('portainer.docker')
.controller('VolumesDatatableController', ['PaginationService', 'DatatableService',
function (PaginationService, DatatableService) {
.controller('VolumesDatatableController', ['$scope', '$controller', 'DatatableService',
function ($scope, $controller, DatatableService) {
angular.extend(this, $controller('GenericDatatableController', {$scope: $scope}));
var ctrl = this;
this.state = {
selectAll: false,
orderBy: this.orderBy,
paginatedItemLimit: PaginationService.getPaginationLimit(this.tableKey),
displayTextFilter: false,
selectedItemCount: 0,
selectedItems: []
};
this.filters = {
usage: {
state: {
open: false,
enabled: false,
showUsedVolumes: true,
@ -22,62 +15,29 @@ function (PaginationService, DatatableService) {
}
};
this.onTextFilterChange = function() {
DatatableService.setDataTableTextFilters(this.tableKey, this.state.textFilter);
};
this.changeOrderBy = function(orderField) {
this.state.reverseOrder = this.state.orderBy === orderField ? !this.state.reverseOrder : false;
this.state.orderBy = orderField;
DatatableService.setDataTableOrder(this.tableKey, orderField, this.state.reverseOrder);
};
this.selectItem = function(item) {
if (item.Checked) {
this.state.selectedItemCount++;
this.state.selectedItems.push(item);
} else {
this.state.selectedItems.splice(this.state.selectedItems.indexOf(item), 1);
this.state.selectedItemCount--;
}
};
this.selectAll = function() {
for (var i = 0; i < this.state.filteredDataSet.length; i++) {
var item = this.state.filteredDataSet[i];
if (item.Checked !== this.state.selectAll) {
item.Checked = this.state.selectAll;
this.selectItem(item);
}
}
};
this.changePaginationLimit = function() {
PaginationService.setPaginationLimit(this.tableKey, this.state.paginatedItemLimit);
};
this.applyFilters = function(value) {
var volume = value;
var filters = ctrl.filters;
if ((volume.dangling && filters.usage.showUnusedVolumes)
|| (!volume.dangling && filters.usage.showUsedVolumes)) {
if ((volume.dangling && filters.state.showUnusedVolumes)
|| (!volume.dangling && filters.state.showUsedVolumes)) {
return true;
}
return false;
};
this.onUsageFilterChange = function() {
var filters = this.filters.usage;
this.onstateFilterChange = function() {
var filters = this.filters.state;
var filtered = false;
if (!filters.showUsedVolumes || !filters.showUnusedVolumes) {
filtered = true;
}
this.filters.usage.enabled = filtered;
this.filters.state.enabled = filtered;
DatatableService.setDataTableFilters(this.tableKey, this.filters);
};
this.$onInit = function() {
setDefaults(this);
this.setDefaults();
this.prepareTableFromDataset();
var storedOrder = DatatableService.getDataTableOrder(this.tableKey);
if (storedOrder !== null) {
@ -85,20 +45,18 @@ function (PaginationService, DatatableService) {
this.state.orderBy = storedOrder.orderBy;
}
var textFilter = DatatableService.getDataTableTextFilters(this.tableKey);
if (textFilter !== null) {
this.state.textFilter = textFilter;
this.onTextFilterChange();
}
var storedFilters = DatatableService.getDataTableFilters(this.tableKey);
if (storedFilters !== null) {
this.filters = storedFilters;
}
this.filters.usage.open = false;
var textFilter = DatatableService.getDataTableTextFilters(this.tableKey);
if (textFilter !== null) {
this.state.textFilter = textFilter;
if (this.filters && this.filters.state) {
this.filters.state.open = false;
}
};
function setDefaults(ctrl) {
ctrl.showTextFilter = ctrl.showTextFilter ? ctrl.showTextFilter : false;
ctrl.state.reverseOrder = ctrl.reverseOrder ? ctrl.reverseOrder : false;
}
}]);

View File

@ -39,7 +39,7 @@
ng-class="{active: item.Checked}">
<td>
<span class="md-checkbox">
<input id="select_{{ $index }}" type="checkbox" ng-model="item.Checked" ng-change="$ctrl.selectItem(item)" />
<input id="select_{{ $index }}" type="checkbox" ng-model="item.Checked" ng-click="$ctrl.selectItem(item, $event)" />
<label for="select_{{ $index }}"></label>
</span>
<a ui-sref="portainer.registries.registry.repository({repository: item.Name})" class="monospaced"

View File

@ -54,7 +54,7 @@
ng-class="{active: item.Checked}">
<td>
<span class="md-checkbox">
<input id="select_{{ $index }}" type="checkbox" ng-model="item.Checked" ng-change="$ctrl.selectItem(item)" />
<input id="select_{{ $index }}" type="checkbox" ng-model="item.Checked" ng-click="$ctrl.selectItem(item, $event)" />
<label for="select_{{ $index }}"></label>
</span>
{{ item.Name }}

View File

@ -1,15 +1,34 @@
angular.module('portainer.docker')
.controller('StoridgeDrivesDatatableController', ['$scope', '$controller',
function ($scope, $controller) {
.controller('StoridgeDrivesDatatableController', ['$scope', '$controller', 'DatatableService',
function ($scope, $controller, DatatableService) {
angular.extend(this, $controller('GenericDatatableController', {$scope: $scope}));
this.selectAll = function() {
for (var i = 0; i < this.state.filteredDataSet.length; i++) {
var item = this.state.filteredDataSet[i];
if (item.Status !== 'normal' && item.Checked !== this.state.selectAll) {
item.Checked = this.state.selectAll;
this.selectItem(item);
}
this.allowSelection = function (item) {
return item.Status !== 'normal';
};
this.$onInit = function() {
this.setDefaults();
this.prepareTableFromDataset();
var storedOrder = DatatableService.getDataTableOrder(this.tableKey);
if (storedOrder !== null) {
this.state.reverseOrder = storedOrder.reverse;
this.state.orderBy = storedOrder.orderBy;
}
var textFilter = DatatableService.getDataTableTextFilters(this.tableKey);
if (textFilter !== null) {
this.state.textFilter = textFilter;
this.onTextFilterChange();
}
var storedFilters = DatatableService.getDataTableFilters(this.tableKey);
if (storedFilters !== null) {
this.filters = storedFilters;
}
if (this.filters && this.filters.state) {
this.filters.state.open = false;
}
};
}

View File

@ -1,6 +1,6 @@
angular.module('portainer.integrations.storidge')
.controller('StoridgeNodesDatatableController', ['$scope', '$controller', 'clipboard', 'Notifications', 'StoridgeNodeService',
function($scope, $controller, clipboard, Notifications, StoridgeNodeService) {
.controller('StoridgeNodesDatatableController', ['$scope', '$controller', 'clipboard', 'Notifications', 'StoridgeNodeService', 'DatatableService',
function($scope, $controller, clipboard, Notifications, StoridgeNodeService, DatatableService) {
angular.extend(this, $controller('GenericDatatableController', { $scope: $scope }));
var ctrl = this;
@ -20,4 +20,29 @@ function($scope, $controller, clipboard, Notifications, StoridgeNodeService) {
$('#copyNotification').show();
$('#copyNotification').fadeOut(2000);
};
this.$onInit = function() {
this.setDefaults();
this.prepareTableFromDataset();
var storedOrder = DatatableService.getDataTableOrder(this.tableKey);
if (storedOrder !== null) {
this.state.reverseOrder = storedOrder.reverse;
this.state.orderBy = storedOrder.orderBy;
}
var textFilter = DatatableService.getDataTableTextFilters(this.tableKey);
if (textFilter !== null) {
this.state.textFilter = textFilter;
this.onTextFilterChange();
}
var storedFilters = DatatableService.getDataTableFilters(this.tableKey);
if (storedFilters !== null) {
this.filters = storedFilters;
}
if (this.filters && this.filters.state) {
this.filters.state.open = false;
}
};
}]);

View File

@ -37,7 +37,7 @@
<tr dir-paginate="item in ($ctrl.state.filteredDataSet = ($ctrl.dataset | filter:$ctrl.state.textFilter | orderBy:$ctrl.state.orderBy:$ctrl.state.reverseOrder | itemsPerPage: $ctrl.state.paginatedItemLimit))" ng-class="{active: item.Checked}">
<td>
<span class="md-checkbox">
<input id="select_{{ $index }}" type="checkbox" ng-model="item.Checked" ng-change="$ctrl.selectItem(item)"/>
<input id="select_{{ $index }}" type="checkbox" ng-model="item.Checked" ng-click="$ctrl.selectItem(item, $event)"/>
<label for="select_{{ $index }}"></label>
</span>
<a ui-sref="storidge.profiles.profile({id: item.Name})">{{ item.Name }}</a>

View File

@ -51,7 +51,7 @@
<tr dir-paginate="item in ($ctrl.state.filteredDataSet = ($ctrl.dataset | filter:$ctrl.state.textFilter | orderBy:$ctrl.state.orderBy:$ctrl.state.reverseOrder | itemsPerPage: $ctrl.state.paginatedItemLimit))" ng-class="{active: item.Checked}">
<td>
<span class="md-checkbox">
<input id="select_{{ $index }}" type="checkbox" ng-model="item.Checked" ng-change="$ctrl.selectItem(item)" ng-disabled="item.Status === 'normal'"/>
<input id="select_{{ $index }}" type="checkbox" ng-model="item.Checked" ng-click="$ctrl.selectItem(item, $event)" ng-disabled="item.Status === 'normal'"/>
<label for="select_{{ $index }}"></label>
</span>
<a ui-sref="docker.volumes.volume.snapshot({snapshotId: item.Id})"> {{ item.Id }}</a>

View File

@ -1,6 +1,6 @@
angular.module('portainer.integrations.storidge').component('storidgeSnapshotsDatatable', {
templateUrl: './storidgeSnapshotsDatatable.html',
controller: 'StoridgeSnapshotsDatatableController',
controller: 'GenericDatatableController',
bindings: {
titleText: '@',
titleIcon: '@',

View File

@ -1,7 +0,0 @@
angular.module('portainer.docker')
.controller('StoridgeSnapshotsDatatableController', ['$scope', '$controller',
function ($scope, $controller) {
angular.extend(this, $controller('GenericDatatableController', {$scope: $scope}));
}
]);

View File

@ -70,7 +70,7 @@
<td>
<span class="md-checkbox">
<input id="select_{{ $index }}" type="checkbox" ng-model="item.Checked" ng-disabled="item.Inherited"
ng-change="$ctrl.selectItem(item)" />
ng-click="$ctrl.selectItem(item, $event)" />
<label for="select_{{ $index }}"></label>
</span>
{{ item.Name }}

View File

@ -1,19 +1,38 @@
angular.module('portainer.app')
.controller('AccessDatatableController', ['$scope', '$controller',
function ($scope, $controller) {
.controller('AccessDatatableController', ['$scope', '$controller', 'DatatableService',
function ($scope, $controller, DatatableService) {
angular.extend(this, $controller('GenericDatatableController', {$scope: $scope}));
this.disableRemove = function(item) {
return item.Inherited;
};
this.selectAll = function() {
for (var i = 0; i < this.state.filteredDataSet.length; i++) {
var item = this.state.filteredDataSet[i];
if (!this.disableRemove(item) && item.Checked !== this.state.selectAll) {
item.Checked = this.state.selectAll;
this.selectItem(item);
}
this.allowSelection = function(item) {
return !this.disableRemove(item);
};
this.$onInit = function() {
this.setDefaults();
this.prepareTableFromDataset();
var storedOrder = DatatableService.getDataTableOrder(this.tableKey);
if (storedOrder !== null) {
this.state.reverseOrder = storedOrder.reverse;
this.state.orderBy = storedOrder.orderBy;
}
var textFilter = DatatableService.getDataTableTextFilters(this.tableKey);
if (textFilter !== null) {
this.state.textFilter = textFilter;
this.onTextFilterChange();
}
var storedFilters = DatatableService.getDataTableFilters(this.tableKey);
if (storedFilters !== null) {
this.filters = storedFilters;
}
if (this.filters && this.filters.state) {
this.filters.state.open = false;
}
};
}

View File

@ -145,6 +145,7 @@
left: 0;
position: absolute;
top: 0;
pointer-events: none;
}
.md-checkbox label:before {

View File

@ -62,7 +62,7 @@
<tr dir-paginate="item in ($ctrl.state.filteredDataSet = ($ctrl.dataset | filter:$ctrl.state.textFilter | orderBy:$ctrl.state.orderBy:$ctrl.state.reverseOrder | itemsPerPage: $ctrl.state.paginatedItemLimit))" ng-class="{active: item.Checked}">
<td>
<span class="md-checkbox" ng-if="$ctrl.endpointManagement">
<input id="select_{{ $index }}" type="checkbox" ng-model="item.Checked" ng-change="$ctrl.selectItem(item)"/>
<input id="select_{{ $index }}" type="checkbox" ng-model="item.Checked" ng-click="$ctrl.selectItem(item, $event)"/>
<label for="select_{{ $index }}"></label>
</span>
<a ui-sref="portainer.endpoints.endpoint({id: item.Id})" ng-if="$ctrl.endpointManagement">{{ item.Name }}</a>

View File

@ -1,5 +1,10 @@
import _ from 'lodash-es';
import './datatable.css';
function isBetween(value, a, b) {
return (value >= a && value <= b) || (value >= b && value <= a) ;
}
angular.module('portainer.app')
.controller('GenericDatatableController', ['PaginationService', 'DatatableService', 'PAGINATION_MAX_ITEMS',
function (PaginationService, DatatableService, PAGINATION_MAX_ITEMS) {
@ -9,7 +14,9 @@ function (PaginationService, DatatableService, PAGINATION_MAX_ITEMS) {
orderBy: this.orderBy,
paginatedItemLimit: PAGINATION_MAX_ITEMS,
displayTextFilter: false,
selectedItemCount: 0,
get selectedItemCount() {
return this.selectedItems.length || 0;
},
selectedItems: []
};
@ -24,32 +31,86 @@ function (PaginationService, DatatableService, PAGINATION_MAX_ITEMS) {
DatatableService.setDataTableOrder(this.tableKey, orderField, this.state.reverseOrder);
};
this.selectItem = function(item) {
if (item.Checked) {
this.state.selectedItemCount++;
this.state.selectedItems.push(item);
} else {
this.state.selectedItems.splice(this.state.selectedItems.indexOf(item), 1);
this.state.selectedItemCount--;
this.selectItem = function(item, event) {
// Handle range select using shift
if (event && event.originalEvent.shiftKey && this.state.firstClickedItem) {
const firstItemIndex = this.state.filteredDataSet.indexOf(this.state.firstClickedItem);
const lastItemIndex = this.state.filteredDataSet.indexOf(item);
const itemsInRange = _.filter(this.state.filteredDataSet, (item, index) => {
return isBetween(index, firstItemIndex, lastItemIndex);
});
const value = item.Checked;
_.forEach(itemsInRange, (i) => {
if (!this.allowSelection(i)) {
return;
}
i.Checked = value;
});
this.state.firstClickedItem = item;
} else if (event) {
this.state.firstClickedItem = item;
}
this.state.selectedItems = this.state.filteredDataSet.filter(i => i.Checked);
if (event && this.state.selectAll && this.state.selectedItems.length !== this.state.filteredDataSet.length) {
this.state.selectAll = false;
}
this.onSelectionChanged();
};
this.selectAll = function() {
this.state.firstClickedItem = null;
for (var i = 0; i < this.state.filteredDataSet.length; i++) {
var item = this.state.filteredDataSet[i];
if (item.Checked !== this.state.selectAll) {
if (this.allowSelection(item) && item.Checked !== this.state.selectAll) {
item.Checked = this.state.selectAll;
this.selectItem(item);
}
}
this.onSelectionChanged();
};
/**
* Override this method to allow/deny selection
*/
this.allowSelection = function(/*item*/) {
return true;
}
/**
* Override this method to prepare data table
*/
this.prepareTableFromDataset = function() {
return;
}
/**
* Override this method to execute code after selection changed on datatable
*/
this.onSelectionChanged = function () {
return;
}
this.changePaginationLimit = function() {
PaginationService.setPaginationLimit(this.tableKey, this.state.paginatedItemLimit);
};
this.setDefaults = function() {
this.showTextFilter = this.showTextFilter ? this.showTextFilter : false;
this.state.reverseOrder = this.reverseOrder ? this.reverseOrder : false;
this.state.paginatedItemLimit = PaginationService.getPaginationLimit(this.tableKey);
}
/**
* Duplicate this function when extending GenericDatatableController
* Extending-controller's bindings are not accessible there
* For more details see the following comments
* https://github.com/portainer/portainer/pull/2877#issuecomment-503333425
* https://github.com/portainer/portainer/pull/2877#issuecomment-503537249
*/
this.$onInit = function() {
setDefaults(this);
this.setDefaults();
this.prepareTableFromDataset();
var storedOrder = DatatableService.getDataTableOrder(this.tableKey);
if (storedOrder !== null) {
@ -62,11 +123,13 @@ function (PaginationService, DatatableService, PAGINATION_MAX_ITEMS) {
this.state.textFilter = textFilter;
this.onTextFilterChange();
}
};
function setDefaults(ctrl) {
ctrl.showTextFilter = ctrl.showTextFilter ? ctrl.showTextFilter : false;
ctrl.state.reverseOrder = ctrl.reverseOrder ? ctrl.reverseOrder : false;
ctrl.state.paginatedItemLimit = PaginationService.getPaginationLimit(ctrl.tableKey);
}
var storedFilters = DatatableService.getDataTableFilters(this.tableKey);
if (storedFilters !== null) {
this.filters = storedFilters;
}
if (this.filters && this.filters.state) {
this.filters.state.open = false;
}
};
}]);

View File

@ -41,7 +41,7 @@
<tr dir-paginate="item in ($ctrl.state.filteredDataSet = ($ctrl.dataset | filter:$ctrl.state.textFilter | orderBy:$ctrl.state.orderBy:$ctrl.state.reverseOrder | itemsPerPage: $ctrl.state.paginatedItemLimit))" ng-class="{active: item.Checked}">
<td>
<span class="md-checkbox">
<input id="select_{{ $index }}" type="checkbox" ng-model="item.Checked" ng-change="$ctrl.selectItem(item)"/>
<input id="select_{{ $index }}" type="checkbox" ng-model="item.Checked" ng-click="$ctrl.selectItem(item, $event)"/>
<label for="select_{{ $index }}"></label>
</span>
<a ui-sref="portainer.groups.group({id: item.Id})">{{ item.Name }}</a>

View File

@ -48,7 +48,7 @@
<tr dir-paginate="item in ($ctrl.state.filteredDataSet = ($ctrl.dataset | filter:$ctrl.state.textFilter | orderBy:$ctrl.state.orderBy:$ctrl.state.reverseOrder | itemsPerPage: $ctrl.state.paginatedItemLimit))" ng-class="{active: item.Checked}">
<td>
<span class="md-checkbox" ng-if="$ctrl.accessManagement">
<input id="select_{{ $index }}" type="checkbox" ng-model="item.Checked" ng-change="$ctrl.selectItem(item)"/>
<input id="select_{{ $index }}" type="checkbox" ng-model="item.Checked" ng-click="$ctrl.selectItem(item, $event)"/>
<label for="select_{{ $index }}"></label>
</span>
<a ui-sref="portainer.registries.registry({id: item.Id})" ng-if="$ctrl.accessManagement">{{ item.Name }}</a>

View File

@ -54,7 +54,7 @@
<tr dir-paginate="item in ($ctrl.state.filteredDataSet = ($ctrl.dataset | filter:$ctrl.state.textFilter | orderBy:$ctrl.state.orderBy:$ctrl.state.reverseOrder | itemsPerPage: $ctrl.state.paginatedItemLimit))" ng-class="{active: item.Checked}">
<td>
<span class="md-checkbox">
<input id="select_{{ $index }}" type="checkbox" ng-model="item.Checked" ng-change="$ctrl.selectItem(item)" ng-disabled="item.JobType !== 1"/>
<input id="select_{{ $index }}" type="checkbox" ng-model="item.Checked" ng-click="$ctrl.selectItem(item, $event)" ng-disabled="!$ctrl.allowSelection(item)"/>
<label for="select_{{ $index }}"></label>
</span>
<span ng-if="item.JobType !== 1">{{ item.Name }}</span>

View File

@ -1,58 +1,39 @@
angular.module('portainer.app')
.controller('SchedulesDatatableController', ['PaginationService', 'DatatableService',
function (PaginationService, DatatableService) {
.controller('SchedulesDatatableController', ['$scope', '$controller', 'DatatableService',
function ($scope, $controller, DatatableService) {
this.state = {
selectAll: false,
orderBy: this.orderBy,
paginatedItemLimit: PaginationService.getPaginationLimit(this.tableKey),
displayTextFilter: false,
selectedItemCount: 0,
selectedItems: []
};
angular.extend(this, $controller('GenericDatatableController', {$scope: $scope}));
this.changeOrderBy = function(orderField) {
this.state.reverseOrder = this.state.orderBy === orderField ? !this.state.reverseOrder : false;
this.state.orderBy = orderField;
DatatableService.setDataTableOrder(this.tableKey, orderField, this.state.reverseOrder);
};
this.selectItem = function(item) {
if (item.Checked) {
this.state.selectedItemCount++;
this.state.selectedItems.push(item);
} else {
this.state.selectedItems.splice(this.state.selectedItems.indexOf(item), 1);
this.state.selectedItemCount--;
}
};
this.selectAll = function() {
for (var i = 0; i < this.state.filteredDataSet.length; i++) {
var item = this.state.filteredDataSet[i];
if (item.JobType ===1 && item.Checked !== this.state.selectAll) {
item.Checked = this.state.selectAll;
this.selectItem(item);
/**
* Do not allow items
*/
this.allowSelection = function(item) {
return item.JobType === 1
}
}
};
this.changePaginationLimit = function() {
PaginationService.setPaginationLimit(this.tableKey, this.state.paginatedItemLimit);
};
this.$onInit = function() {
this.setDefaults();
this.prepareTableFromDataset();
this.$onInit = function() {
setDefaults(this);
var storedOrder = DatatableService.getDataTableOrder(this.tableKey);
if (storedOrder !== null) {
this.state.reverseOrder = storedOrder.reverse;
this.state.orderBy = storedOrder.orderBy;
}
var storedOrder = DatatableService.getDataTableOrder(this.tableKey);
if (storedOrder !== null) {
this.state.reverseOrder = storedOrder.reverse;
this.state.orderBy = storedOrder.orderBy;
}
};
var textFilter = DatatableService.getDataTableTextFilters(this.tableKey);
if (textFilter !== null) {
this.state.textFilter = textFilter;
this.onTextFilterChange();
}
function setDefaults(ctrl) {
ctrl.showTextFilter = ctrl.showTextFilter ? ctrl.showTextFilter : false;
ctrl.state.reverseOrder = ctrl.reverseOrder ? ctrl.reverseOrder : false;
var storedFilters = DatatableService.getDataTableFilters(this.tableKey);
if (storedFilters !== null) {
this.filters = storedFilters;
}
if (this.filters && this.filters.state) {
this.filters.state.open = false;
}
};
}
}]);
]);

View File

@ -55,7 +55,7 @@
<tr dir-paginate="item in ($ctrl.state.filteredDataSet = ($ctrl.dataset | filter:$ctrl.state.textFilter | orderBy:$ctrl.state.orderBy:$ctrl.state.reverseOrder | itemsPerPage: $ctrl.state.paginatedItemLimit))" ng-class="{active: item.Checked}">
<td>
<span class="md-checkbox" ng-if="!$ctrl.offlineMode" authorization="PortainerStackCreate, PortainerStackDelete">
<input id="select_{{ $index }}" type="checkbox" ng-model="item.Checked" ng-change="$ctrl.selectItem(item)" ng-disabled="item.External && item.Type === 2"/>
<input id="select_{{ $index }}" type="checkbox" ng-model="item.Checked" ng-click="$ctrl.selectItem(item, $event)" ng-disabled="!$ctrl.allowSelection(item)"/>
<label for="select_{{ $index }}"></label>
</span>
<a ng-if="!$ctrl.offlineMode" ui-sref="portainer.stacks.stack({ name: item.Name, id: item.Id, type: item.Type, external: item.External })">{{ item.Name }}</a>

View File

@ -1,52 +1,19 @@
angular.module('portainer.app')
.controller('StacksDatatableController', ['PaginationService', 'DatatableService',
function (PaginationService, DatatableService) {
.controller('StacksDatatableController', ['$scope', '$controller', 'DatatableService',
function ($scope, $controller, DatatableService) {
this.state = {
selectAll: false,
orderBy: this.orderBy,
paginatedItemLimit: PaginationService.getPaginationLimit(this.tableKey),
displayTextFilter: false,
selectedItemCount: 0,
selectedItems: []
};
angular.extend(this, $controller('GenericDatatableController', {$scope: $scope}));
this.onTextFilterChange = function() {
DatatableService.setDataTableTextFilters(this.tableKey, this.state.textFilter);
};
this.changeOrderBy = function(orderField) {
this.state.reverseOrder = this.state.orderBy === orderField ? !this.state.reverseOrder : false;
this.state.orderBy = orderField;
DatatableService.setDataTableOrder(this.tableKey, orderField, this.state.reverseOrder);
};
this.selectItem = function(item) {
if (item.Checked) {
this.state.selectedItemCount++;
this.state.selectedItems.push(item);
} else {
this.state.selectedItems.splice(this.state.selectedItems.indexOf(item), 1);
this.state.selectedItemCount--;
}
};
this.selectAll = function() {
for (var i = 0; i < this.state.filteredDataSet.length; i++) {
var item = this.state.filteredDataSet[i];
if (!(item.External && item.Type === 2) && item.Checked !== this.state.selectAll) {
item.Checked = this.state.selectAll;
this.selectItem(item);
}
}
};
this.changePaginationLimit = function() {
PaginationService.setPaginationLimit(this.tableKey, this.state.paginatedItemLimit);
};
/**
* Do not allow external items
*/
this.allowSelection = function(item) {
return !(item.External && item.Type === 2);
}
this.$onInit = function() {
setDefaults(this);
this.setDefaults();
this.prepareTableFromDataset();
var storedOrder = DatatableService.getDataTableOrder(this.tableKey);
if (storedOrder !== null) {
@ -57,11 +24,16 @@ function (PaginationService, DatatableService) {
var textFilter = DatatableService.getDataTableTextFilters(this.tableKey);
if (textFilter !== null) {
this.state.textFilter = textFilter;
this.onTextFilterChange();
}
var storedFilters = DatatableService.getDataTableFilters(this.tableKey);
if (storedFilters !== null) {
this.filters = storedFilters;
}
if (this.filters && this.filters.state) {
this.filters.state.open = false;
}
};
function setDefaults(ctrl) {
ctrl.showTextFilter = ctrl.showTextFilter ? ctrl.showTextFilter : false;
ctrl.state.reverseOrder = ctrl.reverseOrder ? ctrl.reverseOrder : false;
}
}]);

View File

@ -37,7 +37,7 @@
<tr dir-paginate="item in ($ctrl.state.filteredDataSet = ($ctrl.dataset | filter:$ctrl.state.textFilter | orderBy:$ctrl.state.orderBy:$ctrl.state.reverseOrder | itemsPerPage: $ctrl.state.paginatedItemLimit))" ng-class="{active: item.Checked}">
<td>
<span class="md-checkbox">
<input id="select_{{ $index }}" type="checkbox" ng-model="item.Checked" ng-change="$ctrl.selectItem(item)"/>
<input id="select_{{ $index }}" type="checkbox" ng-model="item.Checked" ng-click="$ctrl.selectItem(item, $event)"/>
<label for="select_{{ $index }}"></label>
</span>
{{ item.Name }}

View File

@ -37,7 +37,7 @@
<tr dir-paginate="item in ($ctrl.state.filteredDataSet = ($ctrl.dataset | filter:$ctrl.state.textFilter | orderBy:$ctrl.state.orderBy:$ctrl.state.reverseOrder | itemsPerPage: $ctrl.state.paginatedItemLimit))" ng-class="{active: item.Checked}">
<td>
<span class="md-checkbox">
<input id="select_{{ $index }}" type="checkbox" ng-model="item.Checked" ng-change="$ctrl.selectItem(item)"/>
<input id="select_{{ $index }}" type="checkbox" ng-model="item.Checked" ng-click="$ctrl.selectItem(item, $event)"/>
<label for="select_{{ $index }}"></label>
</span>
<a ui-sref="portainer.teams.team({id: item.Id})">{{ item.Name }}</a>

View File

@ -51,7 +51,7 @@
<tr dir-paginate="item in ($ctrl.state.filteredDataSet = ($ctrl.dataset | filter:$ctrl.state.textFilter | orderBy:$ctrl.state.orderBy:$ctrl.state.reverseOrder | itemsPerPage: $ctrl.state.paginatedItemLimit))" ng-class="{active: item.Checked}">
<td>
<span class="md-checkbox">
<input id="select_{{ $index }}" type="checkbox" ng-model="item.Checked" ng-change="$ctrl.selectItem(item)"/>
<input id="select_{{ $index }}" type="checkbox" ng-model="item.Checked" ng-click="$ctrl.selectItem(item, $event)"/>
<label for="select_{{ $index }}"></label>
</span>
<a ui-sref="portainer.users.user({id: item.Id})">{{ item.Username }}</a>