From 9e785ab019b5b46f047a7c2f91843823231e946f Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Wed, 24 Jun 2015 16:48:05 -0400 Subject: [PATCH 1/7] Make multiselect dropdown faster and nicer for large lists --- static/directives/multiselect-dropdown.html | 9 ++++++++- static/directives/new-header-bar.html | 20 ++++++++++---------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/static/directives/multiselect-dropdown.html b/static/directives/multiselect-dropdown.html index 11acda579..c4fbf4f74 100644 --- a/static/directives/multiselect-dropdown.html +++ b/static/directives/multiselect-dropdown.html @@ -11,13 +11,20 @@ -
  • +
  • +
  • +
    +
    + + {{ (items | filter:filter).length - 10 }} additional +
    +
    +
  • No matching {{ itemName }}s found
    diff --git a/static/directives/new-header-bar.html b/static/directives/new-header-bar.html index 37bc74e4b..f59baca39 100644 --- a/static/directives/new-header-bar.html +++ b/static/directives/new-header-bar.html @@ -5,7 +5,7 @@ - + @@ -19,29 +19,29 @@
  • - Sign in + Sign in
  • From 43330bcfadf094a715c4ef699141ddb2541fb43f Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Wed, 24 Jun 2015 17:07:38 -0400 Subject: [PATCH 2/7] Make loading of the tags tab async --- static/directives/repo-view/repo-panel-tags.html | 4 +++- static/js/directives/repo-view/repo-panel-tags.js | 2 ++ static/js/pages/repo-view.js | 7 +++++++ static/partials/repo-view.html | 6 ++++-- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/static/directives/repo-view/repo-panel-tags.html b/static/directives/repo-view/repo-panel-tags.html index af727544c..0aa650731 100644 --- a/static/directives/repo-view/repo-panel-tags.html +++ b/static/directives/repo-view/repo-panel-tags.html @@ -57,7 +57,9 @@ - +
    + +
    diff --git a/static/js/directives/filters/slice.js b/static/js/directives/filters/slice.js new file mode 100644 index 000000000..81fd8b4ac --- /dev/null +++ b/static/js/directives/filters/slice.js @@ -0,0 +1,8 @@ +/** + * Slice filter. + */ +angular.module('quay').filter('slice', function() { + return function(arr, start, end) { + return (arr || []).slice(start, end); + }; +}); \ No newline at end of file diff --git a/static/js/directives/repo-view/repo-panel-tags.js b/static/js/directives/repo-view/repo-panel-tags.js index f1a76e383..98ef1ac71 100644 --- a/static/js/directives/repo-view/repo-panel-tags.js +++ b/static/js/directives/repo-view/repo-panel-tags.js @@ -24,7 +24,8 @@ angular.module('quay').directive('repoPanelTags', function () { $scope.checkedTags = UIService.createCheckStateController([], 'name'); $scope.options = { 'predicate': 'last_modified_datetime', - 'reverse': false + 'reverse': false, + 'page': 0 }; $scope.iterationState = {}; diff --git a/static/js/directives/ui/page-controls.js b/static/js/directives/ui/page-controls.js new file mode 100644 index 000000000..aeeed0a79 --- /dev/null +++ b/static/js/directives/ui/page-controls.js @@ -0,0 +1,41 @@ +/** + * An element which displays controls for moving between pages of paginated results. + */ +angular.module('quay').directive('pageControls', function () { + var directiveDefinitionObject = { + priority: 0, + templateUrl: '/static/directives/page-controls.html', + replace: false, + transclude: true, + restrict: 'C', + scope: { + 'currentPage': '=currentPage', + 'pageSize': '=pageSize', + 'totalCount': '=totalCount' + }, + controller: function($scope, $element) { + $scope.getPageStart = function(currentPage, pageSize, totalCount) { + return Math.min((currentPage * pageSize) + 1, totalCount); + }; + + $scope.getPageEnd = function(currentPage, pageSize, totalCount) { + return Math.min(((currentPage + 1) * pageSize), totalCount); + }; + + $scope.getPageCount = function(pageSize, totalCount) { + return Math.ceil(totalCount / pageSize); + }; + + $scope.changePage = function(offset) { + $scope.currentPage += offset; + $scope.currentPage = Math.max($scope.currentPage, 0); + $scope.currentPage = Math.min($scope.currentPage, $scope.getPageCount($scope.pageSize, $scope.totalCount)); + }; + + $scope.setPage = function(page) { + $scope.currentPage = page; + }; + } + }; + return directiveDefinitionObject; +}); \ No newline at end of file From 2ef105dd8c345ad4b0c3beac26ece8c443fbe4a4 Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Thu, 25 Jun 2015 16:09:50 -0400 Subject: [PATCH 6/7] Change binds on the tags page to `bindonce` This makes building the DOM in Angular faster --- static/directives/image-link.html | 4 +- .../directives/repo-view/repo-panel-tags.html | 41 ++++++++++--------- 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/static/directives/image-link.html b/static/directives/image-link.html index a41bbd33d..93b059a95 100644 --- a/static/directives/image-link.html +++ b/static/directives/image-link.html @@ -1,2 +1,2 @@ -{{ imageId.substr(0, 12) }} \ No newline at end of file +{{ imageId.substr(0, 12) }} \ 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 644ecadf8..a99681846 100644 --- a/static/directives/repo-view/repo-panel-tags.html +++ b/static/directives/repo-view/repo-panel-tags.html @@ -89,40 +89,41 @@ + ng-class="checkedTags.isChecked(tag, checkedTags.checked) ? 'checked' : ''" + bindonce> - + - + - - From 0baaacb0a6f76805b0e5af429f1a67ce585f1174 Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Thu, 25 Jun 2015 16:16:19 -0400 Subject: [PATCH 7/7] Change delete tags dialog to show tabs in a row Fixes #177 --- static/css/directives/ui/tag-operations-dialog.css | 11 +++++++++++ static/directives/tag-operations-dialog.html | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/static/css/directives/ui/tag-operations-dialog.css b/static/css/directives/ui/tag-operations-dialog.css index 75a52b730..861a3d89d 100644 --- a/static/css/directives/ui/tag-operations-dialog.css +++ b/static/css/directives/ui/tag-operations-dialog.css @@ -9,4 +9,15 @@ margin-bottom: 20px; margin-top: 10px; position: relative; +} + +.tag-operations-dialog .delete-tag-list { + margin: 4px; + padding: 0px; +} + +.tag-operations-dialog .delete-tag-list li { + list-style: none; + display: inline-block; + margin: 4px; } \ No newline at end of file diff --git a/static/directives/tag-operations-dialog.html b/static/directives/tag-operations-dialog.html index 54dddd698..a769d5fa1 100644 --- a/static/directives/tag-operations-dialog.html +++ b/static/directives/tag-operations-dialog.html @@ -79,7 +79,7 @@ dialog-title="Delete Tags" dialog-action-title="Delete Tags"> Are you sure you want to delete the following tags: -
      +
      • {{ tag_info.name }}
    diff --git a/static/js/directives/repo-view/repo-panel-tags.js b/static/js/directives/repo-view/repo-panel-tags.js index 55d3183d3..181510578 100644 --- a/static/js/directives/repo-view/repo-panel-tags.js +++ b/static/js/directives/repo-view/repo-panel-tags.js @@ -14,6 +14,8 @@ angular.module('quay').directive('repoPanelTags', function () { 'imagesResource': '=imagesResource', 'images': '=images', + 'isEnabled': '=isEnabled', + 'getImages': '&getImages' }, controller: function($scope, $element, $filter, $location, ApiService, UIService) { diff --git a/static/js/pages/repo-view.js b/static/js/pages/repo-view.js index fdc579994..aff4f0997 100644 --- a/static/js/pages/repo-view.js +++ b/static/js/pages/repo-view.js @@ -20,6 +20,7 @@ $scope.imagesRequired = false; // Tab-enabled counters. + $scope.tagsShown = 0; $scope.logsShown = 0; $scope.buildsShown = 0; $scope.settingsShown = 0; @@ -158,6 +159,12 @@ $scope.logsShown++; }; + $scope.showTags = function() { + $timeout(function() { + $scope.tagsShown = 1; + }, 10); + }; + $scope.requireImages = function() { // Lazily load the repo's images if this is the first call to a tab // that needs the images. diff --git a/static/partials/repo-view.html b/static/partials/repo-view.html index d3beccd10..2ed0f1333 100644 --- a/static/partials/repo-view.html +++ b/static/partials/repo-view.html @@ -21,7 +21,8 @@ - + @@ -66,7 +67,8 @@ images="viewScope.images" images-resource="viewScope.imagesResource" selected-tags="viewScope.selectedTags" - get-images="getImages(callback)"> + get-images="getImages(callback)" + is-enabled="tagsShown"> From dde8d329846387a1db30a42a0d4e83ff9943ae9e Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Wed, 24 Jun 2015 17:43:57 -0400 Subject: [PATCH 3/7] Switch CheckStateController to not be O(n) for the isChecked call --- .../directives/repo-view/repo-panel-tags.js | 8 +++--- static/js/services/ui-service.js | 25 +++++++++++++++++-- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/static/js/directives/repo-view/repo-panel-tags.js b/static/js/directives/repo-view/repo-panel-tags.js index 181510578..f1a76e383 100644 --- a/static/js/directives/repo-view/repo-panel-tags.js +++ b/static/js/directives/repo-view/repo-panel-tags.js @@ -21,7 +21,7 @@ angular.module('quay').directive('repoPanelTags', function () { controller: function($scope, $element, $filter, $location, ApiService, UIService) { var orderBy = $filter('orderBy'); - $scope.checkedTags = UIService.createCheckStateController([]); + $scope.checkedTags = UIService.createCheckStateController([], 'name'); $scope.options = { 'predicate': 'last_modified_datetime', 'reverse': false @@ -102,7 +102,7 @@ angular.module('quay').directive('repoPanelTags', function () { $scope.tags = ordered; $scope.allTags = allTags; - $scope.checkedTags = UIService.createCheckStateController(ordered, checked); + $scope.checkedTags = UIService.createCheckStateController(ordered, 'name', checked); $scope.checkedTags.listen(function(checked) { $scope.selectedTags = checked.map(function(tag_info) { return tag_info.name; @@ -117,9 +117,9 @@ angular.module('quay').directive('repoPanelTags', function () { $scope.$watch('selectedTags', function(selectedTags) { if (!selectedTags || !$scope.repository || !$scope.imageMap) { return; } - $scope.checkedTags.checked = selectedTags.map(function(tag) { + $scope.checkedTags.setChecked(selectedTags.map(function(tag) { return $scope.repository.tags[tag]; - }); + })); }, true); $scope.$watch('repository', function(repository) { diff --git a/static/js/services/ui-service.js b/static/js/services/ui-service.js index 86a326390..792f8ad11 100644 --- a/static/js/services/ui-service.js +++ b/static/js/services/ui-service.js @@ -2,10 +2,14 @@ * Service which provides helper methods for performing some simple UI operations. */ angular.module('quay').factory('UIService', ['$timeout', '$rootScope', '$location', function($timeout, $rootScope, $location) { - var CheckStateController = function(items, opt_checked) { + var CheckStateController = function(items, itemKey, opt_checked) { this.items = items; + this.itemKey = itemKey; this.checked = opt_checked || []; + this.checkedMap = {}; this.listeners_ = []; + + this.buildMap_(); }; CheckStateController.prototype.listen = function(callback) { @@ -13,7 +17,7 @@ angular.module('quay').factory('UIService', ['$timeout', '$rootScope', '$locatio }; CheckStateController.prototype.isChecked = function(item) { - return $.inArray(item, this.checked) >= 0; + return !!this.checkedMap[item[this.itemKey]]; }; CheckStateController.prototype.toggleItem = function(item) { @@ -27,19 +31,35 @@ angular.module('quay').factory('UIService', ['$timeout', '$rootScope', '$locatio CheckStateController.prototype.toggleItems = function() { if (this.checked.length) { this.checked = []; + this.checkedMap = {}; } else { this.checked = this.items.slice(); + this.buildMap_(); } this.callListeners_(); }; + CheckStateController.prototype.setChecked = function(items) { + this.checked = items.slice(); + this.buildMap_(); + }; + + CheckStateController.prototype.buildMap_ = function() { + var that = this; + this.checked.forEach(function(item) { + that.checkedMap[item[that.itemKey]] = true; + }); + }; + CheckStateController.prototype.checkByFilter = function(filter) { this.checked = $.grep(this.items, filter); + this.buildMap_(); this.callListeners_(); }; CheckStateController.prototype.checkItem = function(item) { this.checked.push(item); + this.checkedMap[item[this.itemKey]] = true; this.callListeners_(); }; @@ -47,6 +67,7 @@ angular.module('quay').factory('UIService', ['$timeout', '$rootScope', '$locatio this.checked = $.grep(this.checked, function(cItem) { return cItem != item; }); + this.checkedMap[item[this.itemKey]] = false; this.callListeners_(); }; From 0d133b0fa4d7a9a7a3a9093dbe9c467695edb59a Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Thu, 25 Jun 2015 13:03:08 -0400 Subject: [PATCH 4/7] appLinkTarget needs to be lazy --- static/directives/new-header-bar.html | 20 ++++++++++---------- static/js/directives/ui/header-bar.js | 11 ++++++++--- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/static/directives/new-header-bar.html b/static/directives/new-header-bar.html index f59baca39..37bc74e4b 100644 --- a/static/directives/new-header-bar.html +++ b/static/directives/new-header-bar.html @@ -5,7 +5,7 @@ - + @@ -19,29 +19,29 @@ diff --git a/static/js/directives/ui/header-bar.js b/static/js/directives/ui/header-bar.js index e5da1073b..98692ffed 100644 --- a/static/js/directives/ui/header-bar.js +++ b/static/js/directives/ui/header-bar.js @@ -110,10 +110,15 @@ angular.module('quay').directive('headerBar', function () { }; $scope.appLinkTarget = function() { - if ($("div[ng-view]").length === 0) { - return "_self"; + if ($scope._appLinkTarget) { + return $scope._appLinkTarget; } - return ""; + + if ($("div[ng-view]").length === 0) { + return $scope._appLinkTarget = "_self"; + } + + return $scope._appLinkTarget = ""; }; $scope.getEnterpriseLogo = function() { From 672e8a5ba90c97bd6a56b57170559f08c610a531 Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Thu, 25 Jun 2015 15:47:37 -0400 Subject: [PATCH 5/7] Add pagination to the tags view --- static/css/core-ui.css | 7 ++++ static/css/directives/ui/page-controls.css | 33 +++++++++++++++ static/directives/page-controls.html | 20 +++++++++ .../directives/repo-view/repo-panel-tags.html | 3 +- static/js/directives/filters/slice.js | 8 ++++ .../directives/repo-view/repo-panel-tags.js | 3 +- static/js/directives/ui/page-controls.js | 41 +++++++++++++++++++ 7 files changed, 113 insertions(+), 2 deletions(-) create mode 100644 static/css/directives/ui/page-controls.css create mode 100644 static/directives/page-controls.html create mode 100644 static/js/directives/filters/slice.js create mode 100644 static/js/directives/ui/page-controls.js diff --git a/static/css/core-ui.css b/static/css/core-ui.css index 441df760e..b2213d91f 100644 --- a/static/css/core-ui.css +++ b/static/css/core-ui.css @@ -1151,8 +1151,15 @@ a:focus { float: right; } +.co-check-bar .co-filter-box .page-controls { + margin-right: 6px; + margin-bottom: 6px; +} + .co-check-bar .co-filter-box input { width: 300px; + display: inline-block; + vertical-align: middle; } .empty { diff --git a/static/css/directives/ui/page-controls.css b/static/css/directives/ui/page-controls.css new file mode 100644 index 000000000..25306a212 --- /dev/null +++ b/static/css/directives/ui/page-controls.css @@ -0,0 +1,33 @@ +.page-controls-element { + display: inline-block +} + +.page-controls { + display: inline-block; +} + +.page-controls-element .current-items { + font-weight: bold; + margin-right: 10px; + padding: 4px; + border: 1px solid transparent; + vertical-align: middle; +} + +.page-controls-element .current-items:hover { + border: 1px solid #ddd; +} + +.page-controls-element .btn { + font-size: 18px; + line-height: 14px; + height: 34px; +} + +.page-controls-element .btn:disabled { + color: #ccc; +} + +.page-controls-element .page-view { + cursor: pointer; +} \ No newline at end of file diff --git a/static/directives/page-controls.html b/static/directives/page-controls.html new file mode 100644 index 000000000..6243924ef --- /dev/null +++ b/static/directives/page-controls.html @@ -0,0 +1,20 @@ +
    + + + {{ getPageStart(currentPage, pageSize, totalCount) }} - {{ getPageEnd(currentPage, pageSize, totalCount) }} + of {{ totalCount }} + + + + + + + +
    \ 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 0aa650731..644ecadf8 100644 --- a/static/directives/repo-view/repo-panel-tags.html +++ b/static/directives/repo-view/repo-panel-tags.html @@ -53,6 +53,7 @@
    + @@ -87,7 +88,7 @@
    {{ tag.name }}
    {{ tag.name }} + - - - Add New Tag - - - Delete Tag + + + + Add New Tag + + + Delete Tag +