Fix handling of large numbers of image tracks (#1451)
Fixes #1448 Image tracks will now automatically inline when possible. When not possible, we display a maximum of 5 tracks before we change them to a single column with colored dots.
This commit is contained in:
parent
94e9448658
commit
f0bf138448
4 changed files with 83 additions and 24 deletions
|
@ -279,6 +279,7 @@ a:focus {
|
|||
}
|
||||
|
||||
.co-tabs li a {
|
||||
display: inline-block;
|
||||
height: 60px;
|
||||
line-height: 60px;
|
||||
white-space: nowrap;
|
||||
|
|
|
@ -57,10 +57,6 @@
|
|||
display: block;
|
||||
}
|
||||
|
||||
.repo-panel-tags-element .image-id-col {
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.repo-panel-tags-element .options-col {
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
</div>
|
||||
|
||||
<div class="cor-checkable-menu-item" item-filter="imageIDFilter(it.image_id, item)"
|
||||
ng-repeat="it in imageTracks">
|
||||
ng-repeat="it in imageTrackEntries">
|
||||
<i class="fa fa-circle-o" ng-style="{'color': it.color}"></i> {{ it.image_id.substr(0, 12) }}
|
||||
</div>
|
||||
</span>
|
||||
|
@ -90,25 +90,30 @@
|
|||
</td>
|
||||
<td class="hidden-xs"
|
||||
ng-class="tablePredicateClass('security_scanned', options.predicate, options.reverse)"
|
||||
style="width: 230px;"
|
||||
style="width: 200px;"
|
||||
quay-require="['SECURITY_SCANNER']">
|
||||
Security Scan
|
||||
</td>
|
||||
<td class="hidden-xs"
|
||||
<td class="hidden-sm hidden-xs"
|
||||
ng-class="tablePredicateClass('size', options.predicate, options.reverse)"
|
||||
style="width: 80px;">
|
||||
<a ng-click="orderBy('size')">Size</a>
|
||||
</td>
|
||||
<td class="hidden-xs hidden-sm"
|
||||
ng-class="tablePredicateClass('image_id', options.predicate, options.reverse)"
|
||||
style="width: 140px;">
|
||||
style="width: 120px;">
|
||||
<a ng-click="orderBy('image_id')">Image</a>
|
||||
</td>
|
||||
<td class="hidden-xs image-track" ng-repeat="it in imageTracks" bindonce></td>
|
||||
<td class="hidden-xs hidden-sm image-track" ng-repeat="it in imageTracks"
|
||||
ng-if="imageTracks.length <= maxTrackCount"></td>
|
||||
<td class="hidden-xs hidden-sm" ng-if="imageTracks.length > maxTrackCount"
|
||||
style="width: 20px; position: relative;">
|
||||
<span class="image-track-dot" style="border-color: #ccc; top: 4px;"></span>
|
||||
</td>
|
||||
<td class="options-col"></td>
|
||||
<td class="options-col"></td>
|
||||
<td class="options-col"></td>
|
||||
<td class="options-col hidden-xs hidden-sm"></td>
|
||||
<td class="hidden-xs hidden-sm" style="width: 4px"></td>
|
||||
</thead>
|
||||
|
||||
<tr class="co-checkable-row"
|
||||
|
@ -121,7 +126,7 @@
|
|||
<span am-time-ago="tag.last_modified" bo-if="tag.last_modified"></span>
|
||||
<span bo-if="!tag.last_modified">Unknown</span>
|
||||
</td>
|
||||
<td quay-require="['SECURITY_SCANNER']" class="security-scan-col">
|
||||
<td quay-require="['SECURITY_SCANNER']" class="security-scan-col hidden-xs">
|
||||
<span class="cor-loader-inline" ng-if="getTagVulnerabilities(tag).loading"></span>
|
||||
<span class="vuln-load-error" ng-if="getTagVulnerabilities(tag).hasError"
|
||||
data-title="The vulnerabilities for this tag could not be retrieved at the present time, try again later"
|
||||
|
@ -193,15 +198,27 @@
|
|||
</span>
|
||||
</span>
|
||||
</td>
|
||||
<td class="hidden-xs" bo-text="tag.size | bytes"></td>
|
||||
<td class="hidden-sm hidden-xs" bo-text="tag.size | bytes"></td>
|
||||
<td class="hidden-xs hidden-sm image-id-col">
|
||||
<span class="image-link" repository="repository" image-id="tag.image_id"></span>
|
||||
</td>
|
||||
<td class="hidden-xs image-track" ng-repeat="it in imageTracks" bindonce>
|
||||
<span class="image-track-dot" bo-if="it.image_id == tag.image_id"
|
||||
bo-style="{'borderColor': it.color}" ng-click="selectTrack(it)"></span>
|
||||
<span class="image-track-line" bo-class="trackLineClass($parent.$index, it)"
|
||||
bo-style="{'borderColor': it.color}"></span>
|
||||
<td class="hidden-xs hidden-sm image-track"
|
||||
ng-if="imageTracks.length > maxTrackCount" bindonce>
|
||||
<span ng-repeat="it in imageTracks">
|
||||
<span ng-repeat="entry in it.entries">
|
||||
<span class="image-track-dot" bo-if="entry.image_id == tag.image_id"
|
||||
bo-style="{'borderColor': entry.color}" ng-click="selectTrack(entry)"></span>
|
||||
</span>
|
||||
</span>
|
||||
</td>
|
||||
<td class="hidden-xs hidden-sm image-track" ng-repeat="it in imageTracks"
|
||||
ng-if="imageTracks.length <= maxTrackCount" bindonce>
|
||||
<span ng-repeat="entry in it.entries">
|
||||
<span class="image-track-dot" bo-if="entry.image_id == tag.image_id"
|
||||
bo-style="{'borderColor': entry.color}" ng-click="selectTrack(entry)"></span>
|
||||
<span class="image-track-line" bo-class="trackLineClass($parent.$parent.$parent.$index, entry)"
|
||||
bo-style="{'borderColor': entry.color}"></span>
|
||||
</span>
|
||||
</td>
|
||||
<td class="options-col">
|
||||
<i class="fa fa-download" data-title="Fetch Tag" bs-tooltip
|
||||
|
|
|
@ -21,6 +21,8 @@ angular.module('quay').directive('repoPanelTags', function () {
|
|||
controller: function($scope, $element, $filter, $location, ApiService, UIService, VulnerabilityService) {
|
||||
var orderBy = $filter('orderBy');
|
||||
|
||||
$scope.maxTrackCount = 5;
|
||||
|
||||
$scope.checkedTags = UIService.createCheckStateController([], 'name');
|
||||
$scope.checkedTags.setPage(0);
|
||||
|
||||
|
@ -45,8 +47,6 @@ angular.module('quay').directive('repoPanelTags', function () {
|
|||
|
||||
var tags = [];
|
||||
var allTags = [];
|
||||
var imageMap = {};
|
||||
var imageTracks = [];
|
||||
|
||||
// Build a list of tags and filtered tags.
|
||||
for (var tag in $scope.repository.tags) {
|
||||
|
@ -71,6 +71,8 @@ angular.module('quay').directive('repoPanelTags', function () {
|
|||
var ordered = orderBy(tags, $scope.options.predicate, $scope.options.reverse);
|
||||
var checked = [];
|
||||
|
||||
var imageMap = {};
|
||||
var imageIndexMap = {};
|
||||
for (var i = 0; i < ordered.length; ++i) {
|
||||
var tagInfo = ordered[i];
|
||||
if (!imageMap[tagInfo.image_id]) {
|
||||
|
@ -79,33 +81,76 @@ angular.module('quay').directive('repoPanelTags', function () {
|
|||
}
|
||||
|
||||
imageMap[tagInfo.image_id].push(tagInfo);
|
||||
|
||||
if ($.inArray(tagInfo.name, $scope.selectedTags) >= 0) {
|
||||
checked.push(tagInfo);
|
||||
}
|
||||
|
||||
if (!imageIndexMap[tagInfo.image_id]) {
|
||||
imageIndexMap[tagInfo.image_id] = {'start': i, 'end': i};
|
||||
}
|
||||
|
||||
imageIndexMap[tagInfo.image_id]['end'] = i;
|
||||
};
|
||||
|
||||
// Calculate the image tracks.
|
||||
var colors = d3.scale.category10();
|
||||
var index = 0;
|
||||
var imageTracks = [];
|
||||
var imageTrackEntries = [];
|
||||
|
||||
imageIDs.sort().map(function(image_id) {
|
||||
if (imageMap[image_id].length >= 2){
|
||||
imageTracks.push({
|
||||
// Create the track entry.
|
||||
var index = imageTrackEntries.length;
|
||||
var trackEntry = {
|
||||
'image_id': image_id,
|
||||
'color': colors(index),
|
||||
'count': imageMap[image_id].length,
|
||||
'tags': imageMap[image_id]
|
||||
});
|
||||
};
|
||||
|
||||
imageTrackEntries.push(trackEntry);
|
||||
|
||||
imageMap[image_id]['color'] = colors(index);
|
||||
var currentImageIndex = imageIndexMap[image_id];
|
||||
|
||||
++index;
|
||||
// Find the track in which we can place the entry, if any.
|
||||
var existingTrack = null;
|
||||
for (var i = 0; i < imageTracks.length; ++i) {
|
||||
// For the current track, ensure that the start and end index
|
||||
// for the current entry is outside of the range of the track's
|
||||
// entries. If so, then we can add the entry to the track.
|
||||
var currentTrack = imageTracks[i];
|
||||
var canAddToCurrentTrack = true;
|
||||
for (var j = 0; j < currentTrack.entries.length; ++j) {
|
||||
var currentTrackEntry = currentTrack.entries[j];
|
||||
var entryInfo = imageIndexMap[currentTrackEntry.image_id];
|
||||
if (Math.max(entryInfo.start, currentImageIndex.start) <= Math.min(entryInfo.end, currentImageIndex.end)) {
|
||||
canAddToCurrentTrack = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (canAddToCurrentTrack) {
|
||||
existingTrack = currentTrack;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Add the entry to the track or create a new track if necessary.
|
||||
if (existingTrack) {
|
||||
existingTrack.entries.push(trackEntry)
|
||||
} else {
|
||||
imageTracks.push({
|
||||
'entries': [trackEntry]
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$scope.imageMap = imageMap;
|
||||
$scope.imageTracks = imageTracks;
|
||||
$scope.imageTrackEntries = imageTrackEntries;
|
||||
|
||||
$scope.options.page = 0;
|
||||
|
||||
$scope.tags = ordered;
|
||||
|
|
Reference in a new issue