- Switch to new typeahead (based on our own branch of it until such time as it gets pulled into the mainline) and add an informative empty message on entity search

- Add better messaging around pushing to empty repos
This commit is contained in:
Joseph Schorr 2014-03-07 21:06:31 -05:00
parent 536a91cbb8
commit 2a8669b2f4
8 changed files with 153 additions and 56 deletions

View file

@ -1538,10 +1538,17 @@ p.editable:hover i {
}
.repo .empty-description {
max-width: 600px;
padding: 6px;
}
.repo .empty-description pre:last-child {
margin-bottom: 0px;
}
.repo .empty-description .panel-default {
margin-top: 20px;
}
.repo dl.dl-horizontal dt {
width: 80px;
padding-right: 10px;
@ -2834,9 +2841,10 @@ p.editable:hover i {
.tt-suggestion {
display: block;
padding: 3px 20px;
cursor: pointer;
}
.tt-suggestion.tt-is-under-cursor {
.tt-suggestion.tt-cursor {
color: #fff;
background-color: #0081c2;
background-image: -moz-linear-gradient(top, #0088cc, #0077b3);
@ -2848,10 +2856,17 @@ p.editable:hover i {
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0)
}
.tt-suggestion.tt-is-under-cursor a {
.tt-suggestion.tt-cursor a {
color: #fff;
}
.tt-empty {
padding: 10px;
font-size: 12px;
color: #aaa;
white-space: nowrap;
}
.tt-suggestion p {
margin: 0;
}

View file

@ -29,7 +29,7 @@
<div class="slideinout" ng-show="currentRepo">
<div style="margin-top: 10px">Dockerfile Location:</div>
<div class="dropdown-select" placeholder="'(Repository Root)'" selected-item="currentLocation"
lookahead-items="locations" handle-input="handleLocationInput(input)">
lookahead-items="locations" handle-input="handleLocationInput(input)" handle-item-selected="handleLocationSelected(datum)">
<!-- Icons -->
<i class="dropdown-select-icon none-icon fa fa-folder-o fa-lg" ng-show="isInvalidLocation"></i>
<i class="dropdown-select-icon none-icon fa fa-folder fa-lg" style="color: black;" ng-show="!isInvalidLocation"></i>

View file

@ -2008,8 +2008,7 @@ quayApp.directive('repoSearch', function () {
++searchToken;
}, true);
var element = $($element[0].childNodes[0]);
element.typeahead({
var repoHound = new Bloodhound({
name: 'repositories',
remote: {
url: '/api/find/repository?query=%QUERY',
@ -2031,22 +2030,36 @@ quayApp.directive('repoSearch', function () {
return datums;
}
},
template: function (datum) {
template = '<div class="repo-mini-listing">';
template += '<i class="fa fa-hdd-o fa-lg"></i>'
template += '<span class="name">' + datum.repo.namespace +'/' + datum.repo.name + '</span>'
if (datum.repo.description) {
template += '<span class="description">' + getFirstTextLine(datum.repo.description) + '</span>'
}
datumTokenizer: function(d) {
return Bloodhound.tokenizers.whitespace(d.val);
},
queryTokenizer: Bloodhound.tokenizers.whitespace
});
repoHound.initialize();
template += '</div>'
return template;
var element = $($element[0].childNodes[0]);
element.typeahead({ 'highlight': true }, {
source: repoHound.ttAdapter(),
templates: {
'suggestion': function (datum) {
template = '<div class="repo-mini-listing">';
template += '<i class="fa fa-hdd-o fa-lg"></i>'
template += '<span class="name">' + datum.repo.namespace +'/' + datum.repo.name + '</span>'
if (datum.repo.description) {
template += '<span class="description">' + getFirstTextLine(datum.repo.description) + '</span>'
}
template += '</div>'
return template;
}
}
});
element.on('typeahead:selected', function (e, datum) {
element.typeahead('setQuery', '');
document.location = '/repository/' + datum.repo.namespace + '/' + datum.repo.name;
element.typeahead('val', '');
$scope.$apply(function() {
$location.path('/repository/' + datum.repo.namespace + '/' + datum.repo.name);
});
});
}
};
@ -2213,7 +2226,7 @@ quayApp.directive('entitySearch', function () {
};
$scope.setEntityInternal = function(entity) {
$(input).typeahead('setQuery', $scope.isPersistent ? entity.name : '');
$(input).typeahead('val', $scope.isPersistent ? entity.name : '');
if ($scope.isPersistent) {
$scope.currentEntity = entity;
@ -2226,8 +2239,7 @@ quayApp.directive('entitySearch', function () {
number++;
var input = $element[0].firstChild.firstChild;
$(input).typeahead({
var entitySearchB = new Bloodhound({
name: 'entities' + number,
remote: {
url: '/api/entities/%QUERY',
@ -2253,24 +2265,57 @@ quayApp.directive('entitySearch', function () {
return datums;
}
},
template: function (datum) {
template = '<div class="entity-mini-listing">';
if (datum.entity.kind == 'user' && !datum.entity.is_robot) {
template += '<i class="fa fa-user fa-lg"></i>';
} else if (datum.entity.kind == 'user' && datum.entity.is_robot) {
template += '<i class="fa fa-wrench fa-lg"></i>';
} else if (datum.entity.kind == 'team') {
template += '<i class="fa fa-group fa-lg"></i>';
}
template += '<span class="name">' + datum.value + '</span>';
if (datum.entity.is_org_member === false && datum.entity.kind == 'user') {
template += '<i class="fa fa-exclamation-triangle" title="User is outside the organization"></i>';
}
template += '</div>';
return template;
datumTokenizer: function(d) {
return Bloodhound.tokenizers.whitespace(d.val);
},
queryTokenizer: Bloodhound.tokenizers.whitespace
});
entitySearchB.initialize();
var counter = 0;
var input = $element[0].firstChild.firstChild;
$(input).typeahead({
'highlight': true
}, {
source: entitySearchB.ttAdapter(),
templates: {
'empty': function(info) {
// Only display the empty dialog if the server load has finished.
if (info.resultKind == 'remote') {
var val = $(input).val();
if (!val) {
return null;
}
if (val.indexOf('@') > 0) {
return '<div class="tt-empty">A Quay.io username (not an e-mail address) must be specified</div>';
}
var robots = $scope.isOrganization ? ', robot accounts' : '';
var teams = ($scope.includeTeams && $scope.isOrganization) ? ' or teams' : '';
return '<div class="tt-empty">No matching Quay.io users' + robots + teams + ' found</div>';
}
return null;
},
'suggestion': function (datum) {
template = '<div class="entity-mini-listing">';
if (datum.entity.kind == 'user' && !datum.entity.is_robot) {
template += '<i class="fa fa-user fa-lg"></i>';
} else if (datum.entity.kind == 'user' && datum.entity.is_robot) {
template += '<i class="fa fa-wrench fa-lg"></i>';
} else if (datum.entity.kind == 'team') {
template += '<i class="fa fa-group fa-lg"></i>';
}
template += '<span class="name">' + datum.value + '</span>';
if (datum.entity.is_org_member === false && datum.entity.kind == 'user') {
template += '<i class="fa fa-exclamation-triangle" title="User is outside the organization"></i>';
}
template += '</div>';
return template;
}}
});
$(input).on('input', function(e) {
@ -2288,7 +2333,7 @@ quayApp.directive('entitySearch', function () {
});
$scope.$watch('clearNow', function() {
$(input).typeahead('setQuery', '');
$(input).typeahead('val', '');
$scope.clearEntityInternal();
});
@ -2722,12 +2767,34 @@ quayApp.directive('dropdownSelect', function ($compile) {
return;
}
$(input).typeahead({
var formattedItems = [];
for (var i = 0; i < items.length; ++i) {
var formattedItem = items[i];
if (typeof formattedItem == 'string') {
formattedItem = {
'value': formattedItem
};
}
formattedItems.push(formattedItem);
}
var dropdownHound = new Bloodhound({
name: 'dropdown-items-' + $rootScope.__dropdownSelectCounter,
local: items,
template: function (datum) {
template = datum['template'] ? datum['template'](datum) : datum['value'];
return template;
local: formattedItems,
datumTokenizer: function(d) {
return Bloodhound.tokenizers.whitespace(d.val || d.value || '');
},
queryTokenizer: Bloodhound.tokenizers.whitespace
});
dropdownHound.initialize();
$(input).typeahead({}, {
source: dropdownHound.ttAdapter(),
templates: {
'suggestion': function (datum) {
template = datum['template'] ? datum['template'](datum) : datum['value'];
return template;
}
}
});
@ -2830,6 +2897,10 @@ quayApp.directive('triggerSetupGithub', function () {
$scope.isInvalidLocation = $scope.locations.indexOf(location) < 0;
};
$scope.handleLocationSelected = function(datum) {
$scope.setLocation(datum['value']);
};
$scope.setLocation = function(location) {
$scope.currentLocation = location;
$scope.trigger['config']['subdir'] = location || '';

7
static/lib/typeahead.bundle.min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -26,7 +26,7 @@
<tr ng-show="canEditMembers">
<td colspan="2">
<span class="entity-search" namespace="orgname" include-teams="false" input-title="'Add a user...'"
<span class="entity-search" namespace="orgname" include-teams="false" input-title="'Add a Quay.io user...'"
entity-selected="addNewMember" is-organization="true"
current-entity="selectedMember"></span>
</td>

View file

@ -82,10 +82,21 @@
</div>
<div class="empty-description" ng-show="repo.can_write">
To push images to this repository:<br><br>
<pre>sudo docker tag <i>0u123imageidgoeshere</i> quay.io/{{repo.namespace}}/{{repo.name}}
sudo docker push quay.io/{{repo.namespace}}/{{repo.name}}</pre>
<div class="panel-default">
<div class="panel-heading">How to push a new image to this repository:</div>
<div class="panel-body">
First login to Quay.io (if you have not done so already):
<pre class="command">sudo docker login quay.io</pre>
Tag an image to this repository:
<pre class="command">sudo docker tag <i>0u123imageidgoeshere</i> quay.io/{{repo.namespace}}/{{repo.name}}</pre>
Push the image to this repository:
<pre class="command">sudo docker push quay.io/{{repo.namespace}}/{{repo.name}}</pre>
</div>
</div>
</div>
</div>
<div class="repo-content" ng-show="!currentTag.image && repo.is_building">

View file

@ -45,6 +45,7 @@
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.1/angular-animate.min.js"></script>
<script src="//cdn.jsdelivr.net/g/bootbox@4.1.0,underscorejs@1.5.2,restangular@1.2.0"></script>
<!-- ,typeahead.js@0.10.1 -->
<script src="static/lib/loading-bar.js"></script>
<script src="static/lib/angular-strap.min.js"></script>
@ -54,12 +55,11 @@
<script src="static/lib/angular-md5.js"></script>
<script src="static/lib/bindonce.min.js"></script>
<script src="static/lib/ansi2html.js"></script>
<script src="static/lib/typeahead.bundle.min.js"></script>
<script src="static/lib/angular-moment.min.js"></script>
<script src="static/lib/angular-cookies.min.js"></script>
<script src="static/lib/typeahead.min.js"></script>
<script src="static/lib/pagedown/Markdown.Converter.js"></script>
<script src="static/lib/pagedown/Markdown.Editor.js"></script>
<script src="static/lib/pagedown/Markdown.Sanitizer.js"></script>