Skip to content

Commit

Permalink
Template and image catalog updates
Browse files Browse the repository at this point in the history
* Use flexbox for the template and image catalogs
* Remove intermediate modal dialog and show template and image
  metadata directly in the card
* Expand template catalog directly on initial create page
* Show loading message for templates
* Truncate long text descriptions

Fixes openshift#2536
Fixes openshift#3116
Fixes openshift#3144
Fixes openshift#3323
  • Loading branch information
spadgett committed Jun 22, 2015
1 parent 67617dd commit 930bdd8
Show file tree
Hide file tree
Showing 19 changed files with 468 additions and 609 deletions.
3 changes: 2 additions & 1 deletion assets/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ For more details on the expected scope arguments, see the source under [app/scri
* tile-click (attribute or class) - for use with the `.tile` class, when anything on the tile is clicked, a simulated click to the `a.tile-target` link will be fired. Recommended use is by adding the `.tile-click` class to get the correct styles on hover.
* click-to-reveal (attribute) - the element will be hidden and a link to show the element will appear instead, link text is customizable
* osc-object (attribute or class) - When the element is clicked it will be shown in the details sidebar. Using as a class is preferred to pick up hover/active styles
* truncate-long-text (element) - truncates text to a limit, optionally on word boundaries, adding a tooltip and ellipsis when the text is truncated
##### Filters
Expand All @@ -126,4 +127,4 @@ We rely on [hawtio-core-navigation](https://github.com/hawtio/hawtio-core-naviga
##### Inject additional content into the page
We include the [hawtio-extension-service](https://github.com/hawtio/hawtio-extension-service). Currently we do not render any extension points, but if there are any locations where you would like to see customizable content, this is how we will add a hook to do that. As hooks are added we will provide a list of them here.
We include the [hawtio-extension-service](https://github.com/hawtio/hawtio-extension-service). Currently we do not render any extension points, but if there are any locations where you would like to see customizable content, this is how we will add a hook to do that. As hooks are added we will provide a list of them here.
2 changes: 1 addition & 1 deletion assets/app/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,6 @@
<script src="scripts/controllers/util/oauth.js"></script>
<script src="scripts/controllers/util/error.js"></script>
<script src="scripts/controllers/util/logout.js"></script>
<script src="scripts/controllers/catalog/templates.js"></script>
<script src="scripts/controllers/catalog/images.js"></script>
<script src="scripts/controllers/create.js"></script>
<script src="scripts/controllers/createProject.js"></script>
Expand All @@ -172,6 +171,7 @@
<script src="scripts/directives/labels.js"></script>
<script src="scripts/directives/templateopt.js"></script>
<script src="scripts/directives/tasks.js"></script>
<script src="scripts/directives/truncate.js"></script>
<script src="scripts/directives/catalog.js"></script>
<script src="scripts/directives/oscObjectDescriber.js"></script>
<script src="scripts/filters/date.js"></script>
Expand Down
4 changes: 3 additions & 1 deletion assets/app/scripts/controllers/catalog/images.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,16 @@ angular.module('openshiftConsole')
};

var imagesForRepos = function(imageRepos, scope) {
var tagAnnotationFilter = $filter('imageStreamTagAnnotation');
angular.forEach(imageRepos, function(imageRepo) {
if (imageRepo.status) {
angular.forEach(imageRepo.status.tags, function(tag) {
var imageRepoTag = tag.tag;
var image = {
imageRepo: imageRepo,
imageRepoTag: imageRepoTag,
name: imageRepo.metadata.name + ":" + imageRepoTag
name: imageRepo.metadata.name + ":" + imageRepoTag,
version: tagAnnotationFilter(imageRepo, 'version', imageRepoTag)
};

if (isBuilder(imageRepo, imageRepoTag)) {
Expand Down
58 changes: 0 additions & 58 deletions assets/app/scripts/controllers/catalog/templates.js

This file was deleted.

81 changes: 49 additions & 32 deletions assets/app/scripts/controllers/create.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,55 +8,72 @@
* Controller of the openshiftConsole
*/
angular.module('openshiftConsole')
.controller('CreateController', function ($scope, DataService, $filter, LabelFilter, $location, Logger) {
$scope.projectTemplates = {};
$scope.openshiftTemplates = {};
.controller('CreateController', function ($scope, DataService, tagsFilter, uidFilter, createFromSourceURLFilter, LabelFilter, $location, Logger) {
var projectTemplates;
var openshiftTemplates;

$scope.templatesByTag = {};
// Templates with the `instant-apps` tag.
$scope.instantApps;

// All templates from the shared or project namespace that aren't instant apps.
// This is displayed in the "Other Templates" section.
$scope.otherTemplates;

// Set to true when shared templates and project templates have finished loading.
$scope.templatesLoaded = false;

$scope.sourceURLPattern = /^((ftp|http|https|git):\/\/(\w+:{0,1}[^\s@]*@)|git@)?([^\s@]+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/;

DataService.list("templates", $scope, function(templates) {
$scope.projectTemplates = templates.by("metadata.name");
templatesByTag();
Logger.info("project templates", $scope.projectTemplates);
projectTemplates = templates.by("metadata.name");
updateTemplates();
Logger.info("project templates", projectTemplates);
});

DataService.list("templates", {namespace: "openshift"}, function(templates) {
$scope.openshiftTemplates = templates.by("metadata.name");
templatesByTag();
Logger.info("openshift templates", $scope.openshiftTemplates);
openshiftTemplates = templates.by("metadata.name");
updateTemplates();
Logger.info("openshift templates", openshiftTemplates);
});

var templatesByTag = function() {
$scope.templatesByTag = {};
var fn = function(template) {
if (template.metadata.annotations && template.metadata.annotations.tags) {
var tags = template.metadata.annotations.tags.split(",");
angular.forEach(tags, function(tag){
tag = $.trim(tag);
// not doing this as a map since we are dealing with things across namespaces that could have collisions on name
$scope.templatesByTag[tag] = $scope.templatesByTag[tag] || [];
$scope.templatesByTag[tag].push(template);
});
function isInstantApp(template) {
var i, tags = tagsFilter(template);
for (i = 0; i < tags.length; i++) {
if (tags[i] === "instant-app") {
return true;
}
}

return false;
}

function updateTemplates() {
// Check if we've loaded templates from both the openshift and project namespaces.
$scope.templatesLoaded = openshiftTemplates && projectTemplates;
$scope.instantApps = {};
$scope.otherTemplates = {};

// Categorize templates as instant apps or "other."
var categorizeTemplates = function(template) {
var uid = uidFilter(template);
if (isInstantApp(template)) {
$scope.instantApps[uid] = template;
} else {
$scope.otherTemplates[uid] = template;
}
};

angular.forEach($scope.projectTemplates, fn);
angular.forEach($scope.openshiftTemplates, fn);
angular.forEach(projectTemplates, categorizeTemplates);
angular.forEach(openshiftTemplates, categorizeTemplates);

Logger.info("templatesByTag", $scope.templatesByTag);
};
Logger.info("instantApps", $scope.instantApps);
Logger.info("otherTemplates", $scope.otherTemplates);
}

$scope.createFromSource = function() {
if($scope.from_source_form.$valid) {
var createURI = URI.expand("/project/{project}/catalog/images{?q*}", {
project: $scope.projectName,
q: {
builderfor: $scope.from_source_url
}
});
$location.url(createURI.toString());
var createFromSourceURL = createFromSourceURLFilter($scope.projectName, $scope.from_source_url);
$location.url(createFromSourceURL);
}
};
});
54 changes: 10 additions & 44 deletions assets/app/scripts/directives/catalog.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,64 +4,30 @@ angular.module('openshiftConsole')
.directive('catalogTemplate', function($location) {
return {
restrict: 'E',
// Replace the catalog-template element so that the tiles are all equal height as flexbox items.
// Otherwise, you have to add the CSS tile classes to catalog-template.
replace: true,
scope: {
template: '=',
project: '='
},
templateUrl: 'views/catalog/_template.html',
link: function(scope, elem, attrs) {
$(".select-template", elem).click(function() {
// Must trigger off of the modal's hidden event to guarantee modal has finished closing before switching screens
$(".modal", elem).on('hidden.bs.modal', function () {
scope.$apply(function() {
var createURI = URI.expand("project/{project}/create/fromtemplate{?q*}", {
project: scope.project,
q: {
name: scope.template.metadata.name,
namespace: scope.template.metadata.namespace
}
});
$location.url(createURI.toString());
});
})
.modal('hide');

});
}
templateUrl: 'views/catalog/_template.html'
};
})
.directive('catalogImage', function($location, Logger) {
.directive('catalogImage', function($location) {
return {
restrict: 'E',
// Replace the catalog-template element so that the tiles are all equal height as flexbox items.
// Otherwise, you have to add the CSS tile classes to catalog-template.
replace: true,
scope: {
image: '=',
imageRepo: '=',
imageTag: '=',
version: '=',
project: '=',
sourceUrl: '='
},
templateUrl: 'views/catalog/_image.html',
link: function(scope, elem, attrs) {
$(".select-image", elem).click(function() {
// Must trigger off of the modal's hidden event to guarantee modal has finished closing before switching screens
$(".modal", elem).on('hidden.bs.modal', function () {
scope.$apply(function() {
Logger.info(scope);
var createURI = URI.expand("/project/{project}/create/fromimage{?q*}", {
project: scope.project,
q: {
imageName: scope.imageRepo.metadata.name,
imageTag: scope.imageTag,
namespace: scope.imageRepo.metadata.namespace,
sourceURL: scope.sourceUrl
}
});
$location.url(createURI.toString());
});
})
.modal('hide');

});
}
templateUrl: 'views/catalog/_image.html'
};
});
38 changes: 38 additions & 0 deletions assets/app/scripts/directives/truncate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
'use strict';

angular.module('openshiftConsole')
// Truncates text to a length, adding a tooltip and an ellipsis if truncated.
// Different than `text-overflow: ellipsis` because it allows for multiline text.
.directive('truncateLongText', function() {
return {
restrict: 'E',
scope: {
content: '=',
limit: '=',
useWordBoundary: '='
},
template: '<span ng-attr-title="{{content}}">{{visibleContent}}<span ng-if="truncated">&hellip;</span></span>',
link: function(scope, elem, attr) {
scope.visibleContent = scope.content;
scope.$watch('content', function(content) {
if (!scope.limit || !content || content.length <= scope.limit) {
scope.truncated = false;
scope.visibleContent = content;
return;
}

scope.truncated = true;
scope.visibleContent = content.substring(0, scope.limit);
if (scope.useWordBoundary !== false) {
// Find the last word break, but don't look more than 10 characters back.
// Make sure we show at least the first 5 characters.
var startIndex = Math.max(4, scope.limit - 10);
var lastSpace = scope.visibleContent.lastIndexOf(' ', startIndex);
if (lastSpace !== -1) {
scope.visibleContent = scope.visibleContent.substring(0, lastSpace);
}
}
});
}
};
});
51 changes: 37 additions & 14 deletions assets/app/scripts/filters/resources.js
Original file line number Diff line number Diff line change
Expand Up @@ -267,20 +267,6 @@ angular.module('openshiftConsole')
}
};
})
.filter('provider', function(annotationFilter) {
return function(resource) {
return annotationFilter(resource, 'provider') ||
(resource && resource.metadata && resource.metadata.namespace);
};
})
.filter('imageStreamTagProvider', function(imageStreamTagAnnotationFilter) {
// Look up the provider in ImageStream.spec.tags[tag].annotations.
// Default to resource.metadata.namespace if no annotation.
return function(resource, /* optional */ tagName) {
return imageStreamTagAnnotationFilter(resource, 'provider', tagName) ||
(resource && resource.metadata && resource.metadata.namespace);
};
})
.filter('imageObjectRef', function(){
return function(objectRef, /* optional */ nsIfUnspecified, shortOutput){
var ns = objectRef.namespace || nsIfUnspecified || "";
Expand Down Expand Up @@ -398,6 +384,43 @@ angular.module('openshiftConsole')
return Navigate.projectOverviewURL(projectName);
};
})
.filter('createFromSourceURL', function() {
return function(projectName, sourceURL) {
var createURI = URI.expand("/project/{project}/catalog/images{?q*}", {
project: projectName,
q: {
builderfor: sourceURL
}
});
return createURI.toString();
};
})
.filter('createFromImageURL', function() {
return function(imageStream, imageTag, projectName, sourceURL) {
var createURI = URI.expand("/project/{project}/create/fromimage{?q*}", {
project: projectName,
q: {
imageName: imageStream.metadata.name,
imageTag: imageTag,
namespace: imageStream.metadata.namespace,
sourceURL: sourceURL
}
});
return createURI.toString();
};
})
.filter('createFromTemplateURL', function() {
return function(template, projectName) {
var createURI = URI.expand("project/{project}/create/fromtemplate{?q*}", {
project: projectName,
q: {
name: template.metadata.name,
namespace: template.metadata.namespace
}
});
return createURI.toString();
};
})
.filter('failureObjectName', function() {
return function(failure) {
if (!failure.data || !failure.data.details) {
Expand Down
Loading

0 comments on commit 930bdd8

Please sign in to comment.