Merge pull request #3092 from quay/joseph.schorr/QUAY-949/image-tracks

Image track improvements
This commit is contained in:
Joseph Schorr 2018-05-23 16:25:48 -04:00 committed by GitHub
commit 86929c16d3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 194 additions and 60 deletions

View file

@ -28,6 +28,22 @@
cursor: pointer;
}
.repo-panel-tags-element .image-track-filled-dot {
display: inline-block;
position: absolute;
top: 15px;
left: 2px;
width: 12px;
height: 12px;
background: white;
z-index: 300;
border-radius: 50%;
cursor: pointer;
}
.repo-panel-tags-element .image-track-line {
position: absolute;
top: 0px;
@ -209,3 +225,37 @@
.repo-panel-tags-element .disabled-option a {
color: #ccc;
}
.repo-panel-tags-element .image-tag-tooltip {
padding-bottom: 4px;
}
.repo-panel-tags-element .image-tag-tooltip .image-tag-tooltip-header {
padding: 4px;
padding-left: 10px;
padding-right: 10px;
border-radius: 4px;
margin-bottom: 10px;
}
.repo-panel-tags-element .image-tag-tooltip .image-tag-tooltip-tags {
list-style: none;
padding: 10px;
padding-top: 0px;
padding-bottom: 0px;
margin-bottom: 4px;
}
.repo-panel-tags-element .image-tag-tooltip .image-tag-tooltip-tags .fa-tag {
margin-right: 8px;
color: #ccc;
vertical-align: middle;
}
.repo-panel-tags-element .image-tag-tooltip .image-tag-tooltip-tags-more {
color: #aaa;
font-size: 14px;
margin-bottom: 4px;
text-align: center;
padding: 4px;
}

View file

@ -0,0 +1,11 @@
<div class="popover image-tag-tooltip" tabindex="-1">
<div class="image-tag-tooltip-header"
ng-style="::{'backgroundColor': trackEntryForImage[tag.image_id].color,
'color': constrastingColor( trackEntryForImage[tag.image_id].color)}">
Image {{ tag.image_id.substr(0, 12) }}
</div>
<ul class="image-tag-tooltip-tags">
<li ng-repeat="tag in imageMap[tag.image_id] | limitTo:5"><i class="fa fa-tag"></i>{{ tag.name }}</li>
</ul>
<div class="image-tag-tooltip-tags-more" ng-if="imageMap[tag.image_id].length > 5">and {{ imageMap[tag.image_id].length - 5 }} more tags</div>
</div>

View file

@ -33,8 +33,8 @@
</div>
<div class="cor-checkable-menu-item" item-filter="imageIDFilter(it.image_id, item)"
ng-repeat="it in imageTrackEntries">
<i class="fa fa-circle-o" ng-style="{'color': it.color}"></i> {{ it.image_id.substr(0, 12) }}
ng-repeat="it in imageTrackEntries" ng-if="::it.visible">
<i class="fa fa-circle-o" ng-style="::{'color': it.color}"></i> {{ ::it.image_id.substr(0, 12) }}
</div>
</span>
@ -116,6 +116,9 @@
style="width: 140px;">
<a ng-click="orderBy('expiration_date')" data-title="When the tag expires" data-container="body" bs-tooltip>Expires</a>
</td>
<td class="hidden-xs hidden-sm" ng-if="imageTracks.length > maxTrackCount"
style="width: 20px; position: relative;">
</td>
<td class="hidden-xs hidden-sm"
ng-class="tablePredicateClass('image_id', options.predicate, options.reverse)"
style="width: 140px;">
@ -123,10 +126,6 @@
</td>
<td class="hidden-xs hidden-sm hidden-md 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="hidden-xs hidden-sm" style="width: 4px"></td>
@ -246,26 +245,38 @@
</td>
<!-- Manifest link -->
<td class="hidden-xs hidden-sm hidden-md image-track"
ng-if="imageTracks.length > maxTrackCount">
<span class="image-track-filled-dot"
ng-if="::trackEntryForImage[tag.image_id]"
ng-style="::{'backgroundColor': trackEntryForImage[tag.image_id].color}"
ng-click="::selectTrack(trackEntryForImage[tag.image_id])"
data-template-url="/static/directives/repo-view/image-tag-tooltip.html"
data-placement="left"
data-trigger="hover"
data-animation="am-flip-x"
data-auto-close="1"
bs-popover></span>
</td>
<td class="hidden-xs hidden-sm image-id-col">
<manifest-link repository="repository" image-id="tag.image_id" manifest-digest="tag.manifest_digest"></manifest-link>
</td>
<td class="hidden-xs hidden-sm hidden-md 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 hidden-md 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>
ng-if="imageTracks.length <= maxTrackCount">
<span class="image-track-dot"
ng-if="::it.entryByImageId[tag.image_id]"
ng-style="::{'borderColor': trackEntryForImage[tag.image_id].color}"
ng-click="::selectTrack(trackEntryForImage[tag.image_id])"
data-template-url="/static/directives/repo-view/image-tag-tooltip.html"
data-placement="left"
data-trigger="hover"
data-animation="am-flip-x"
data-auto-close="1"
bs-popover></span>
<span class="image-track-line"
ng-if="::getTrackEntryForIndex(it, $parent.$parent.$index)"
ng-class="::trackLineClass(it, $parent.$parent.$parent.$index)"
ng-style="::{'borderColor': getTrackEntryForIndex(it, $parent.$parent.$parent.$index).color}"></span>
</td>
<td class="options-col">
<i class="fa fa-download" data-title="Fetch Tag" bs-tooltip
@ -315,11 +326,10 @@
</td>
<td class="hidden-xs hidden-sm hidden-md image-track" ng-repeat="it in imageTracks"
ng-if="imageTracks.length <= maxTrackCount" bindonce>
<span ng-repeat="entry in it.entries">
<span class="image-track-line"
bo-class="trackLineExpandedClass($parent.$parent.$parent.$index, entry)"
bo-style="{'borderColor': entry.color}"></span>
</span>
ng-if="::getTrackEntryForIndex(it, $parent.$parent.$index)"
ng-class="::trackLineExpandedClass(it, $parent.$parent.$parent.$index)"
ng-style="::{'borderColor': getTrackEntryForIndex(it, $parent.$parent.$parent.$index).color}"></span>
</td>
<td colspan="1" class="hidden-xs hidden-sm hidden-md"></td>
</tr>

View file

@ -110,24 +110,33 @@ angular.module('quay').directive('repoPanelTags', function () {
// Calculate the image tracks.
var colors = d3.scale.category10();
if (Object.keys(imageMap).length > 10) {
colors = d3.scale.category20();
}
var imageTracks = [];
var imageTrackEntries = [];
var trackEntryForImage = {};
var visibleStartIndex = ($scope.options.page * $scope.tagsPerPage);
var visibleEndIndex = (($scope.options.page + 1) * $scope.tagsPerPage);
imageIDs.sort().map(function(image_id) {
if (imageMap[image_id].length >= 2){
// Create the track entry.
var index = imageTrackEntries.length;
var imageIndexRange = imageIndexMap[image_id];
var colorIndex = imageTrackEntries.length;
var trackEntry = {
'image_id': image_id,
'color': colors(index),
'color': colors(colorIndex),
'count': imageMap[image_id].length,
'tags': imageMap[image_id]
'tags': imageMap[image_id],
'index_range': imageIndexRange,
'visible': visibleStartIndex <= imageIndexRange.end && imageIndexRange.start <= visibleEndIndex,
};
imageTrackEntries.push(trackEntry);
imageMap[image_id]['color'] = colors(index);
var currentImageIndex = imageIndexMap[image_id];
trackEntryForImage[image_id] = trackEntry;
imageMap[image_id]['color'] = colors(colorIndex);
// Find the track in which we can place the entry, if any.
var existingTrack = null;
@ -140,7 +149,7 @@ angular.module('quay').directive('repoPanelTags', function () {
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)) {
if (Math.max(entryInfo.start, imageIndexRange.start) <= Math.min(entryInfo.end, imageIndexRange.end)) {
canAddToCurrentTrack = false;
break;
}
@ -155,17 +164,38 @@ angular.module('quay').directive('repoPanelTags', function () {
// Add the entry to the track or create a new track if necessary.
if (existingTrack) {
existingTrack.entries.push(trackEntry)
existingTrack.entryByImageId[image_id] = trackEntry;
existingTrack.endIndex = Math.max(existingTrack.endIndex, imageIndexRange.end);
for (var j = imageIndexRange.start; j <= imageIndexRange.end; j++) {
existingTrack.entryByIndex[j] = trackEntry;
}
} else {
var entryByImageId = {};
entryByImageId[image_id] = trackEntry;
var entryByIndex = {};
for (var j = imageIndexRange.start; j <= imageIndexRange.end; j++) {
entryByIndex[j] = trackEntry;
}
imageTracks.push({
'entries': [trackEntry]
'entries': [trackEntry],
'entryByImageId': entryByImageId,
'startIndex': imageIndexRange.start,
'endIndex': imageIndexRange.end,
'entryByIndex': entryByIndex,
});
}
imageTrackEntries.push(trackEntry);
}
});
$scope.imageMap = imageMap;
$scope.imageTracks = imageTracks;
$scope.imageTrackEntries = imageTrackEntries;
$scope.trackEntryForImage = trackEntryForImage;
$scope.options.page = 0;
@ -293,48 +323,77 @@ angular.module('quay').directive('repoPanelTags', function () {
$scope.checkedTags.setChecked($scope.tags);
};
$scope.trackLineExpandedClass = function(index, track_info) {
var startIndex = $.inArray(track_info.tags[0], $scope.tags);
var endIndex = $.inArray(track_info.tags[track_info.tags.length - 1], $scope.tags);
index += $scope.options.page * $scope.tagsPerPage;
$scope.constrastingColor = function(backgroundColor) {
// From: https://stackoverflow.com/questions/11068240/what-is-the-most-efficient-way-to-parse-a-css-color-in-javascript
function parseColor(input) {
m = input.match(/^#([0-9a-f]{6})$/i)[1];
return [
parseInt(m.substr(0,2),16),
parseInt(m.substr(2,2),16),
parseInt(m.substr(4,2),16)
];
}
if (index < startIndex) {
var rgb = parseColor(backgroundColor);
// From W3C standard.
var o = Math.round(((parseInt(rgb[0]) * 299) + (parseInt(rgb[1]) * 587) + (parseInt(rgb[2]) * 114)) / 1000);
return (o > 150) ? 'black' : 'white';
};
$scope.getTrackEntryForIndex = function(it, index) {
index += $scope.options.page * $scope.tagsPerPage;
return it.entryByIndex[index];
};
$scope.trackLineExpandedClass = function(it, index, track_info) {
var entry = $scope.getTrackEntryForIndex(it, index);
if (!entry) {
return '';
}
var adjustedIndex = index + ($scope.options.page * $scope.tagsPerPage);
if (index < entry.index_range.start) {
return 'before';
}
if (index > endIndex) {
if (index > entry.index_range.end) {
return 'after';
}
if (index >= startIndex && index < endIndex) {
if (index >= entry.index_range.start && index < entry.index_range.end) {
return 'middle';
}
return '';
};
$scope.trackLineClass = function(index, track_info) {
var startIndex = $.inArray(track_info.tags[0], $scope.tags);
var endIndex = $.inArray(track_info.tags[track_info.tags.length - 1], $scope.tags);
index += $scope.options.page * $scope.tagsPerPage;
$scope.trackLineClass = function(it, index) {
var entry = $scope.getTrackEntryForIndex(it, index);
if (!entry) {
return '';
}
if (index == startIndex) {
var adjustedIndex = index + ($scope.options.page * $scope.tagsPerPage);
if (index == entry.index_range.start) {
return 'start';
}
if (index == endIndex) {
if (index == entry.index_range.end) {
return 'end';
}
if (index > startIndex && index < endIndex) {
if (index > entry.index_range.start && index < entry.index_range.end) {
return 'middle';
}
if (index < startIndex) {
if (index < entry.index_range.start) {
return 'before';
}
if (index > endIndex) {
if (index > entry.index_range.end) {
return 'after';
}
};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long