diff --git a/static/css/directives/repo-view/repo-panel-tags.css b/static/css/directives/repo-view/repo-panel-tags.css index 58872d33f..8dea9ae7e 100644 --- a/static/css/directives/repo-view/repo-panel-tags.css +++ b/static/css/directives/repo-view/repo-panel-tags.css @@ -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; @@ -208,4 +224,38 @@ .repo-panel-tags-element .disabled-option, .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; } \ No newline at end of file diff --git a/static/directives/repo-view/image-tag-tooltip.html b/static/directives/repo-view/image-tag-tooltip.html new file mode 100644 index 000000000..3b1051d06 --- /dev/null +++ b/static/directives/repo-view/image-tag-tooltip.html @@ -0,0 +1,11 @@ +
+
+ Image {{ tag.image_id.substr(0, 12) }} +
+ +
and {{ imageMap[tag.image_id].length - 5 }} more tags
+
\ No newline at end of file diff --git a/static/directives/repo-view/repo-panel-tags.html b/static/directives/repo-view/repo-panel-tags.html index 081e801eb..873be00e6 100644 --- a/static/directives/repo-view/repo-panel-tags.html +++ b/static/directives/repo-view/repo-panel-tags.html @@ -33,8 +33,8 @@
- {{ it.image_id.substr(0, 12) }} + ng-repeat="it in imageTrackEntries" ng-if="::it.visible"> + {{ ::it.image_id.substr(0, 12) }}
@@ -116,6 +116,9 @@ style="width: 140px;"> Expires + + @@ -123,10 +126,6 @@ - - - @@ -246,26 +245,38 @@ + + + - - - - - - - - - - - + ng-if="imageTracks.length <= maxTrackCount"> + + - - - + diff --git a/static/js/directives/repo-view/repo-panel-tags.js b/static/js/directives/repo-view/repo-panel-tags.js index d9182eaaa..f765df037 100644 --- a/static/js/directives/repo-view/repo-panel-tags.js +++ b/static/js/directives/repo-view/repo-panel-tags.js @@ -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'; } };