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 @@
+
\ 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 @@
@@ -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';
}
};