This is an automated email from the ASF dual-hosted git repository. heneveld pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/brooklyn-ui.git
commit 2ab3c4af4b57927253f62914138c9cc74a8fccee Author: Alex Heneveld <[email protected]> AuthorDate: Fri Jun 21 10:31:39 2024 +0100 merge iuliana's uploader enhancements, adding a couple things - updates catalog after save - shows like a modal rather than a full screen - show failure more clearly --- .../catalog/app/views/catalog/catalog.state.js | 14 +++- .../utils/catalog-uploader/catalog-uploader.html | 94 +++++++++++++-------- .../utils/catalog-uploader/catalog-uploader.js | 24 ++++-- .../utils/catalog-uploader/catalog-uploader.less | 95 +++++++++++++++------- 4 files changed, 150 insertions(+), 77 deletions(-) diff --git a/ui-modules/catalog/app/views/catalog/catalog.state.js b/ui-modules/catalog/app/views/catalog/catalog.state.js index 64534312..1d931f0e 100644 --- a/ui-modules/catalog/app/views/catalog/catalog.state.js +++ b/ui-modules/catalog/app/views/catalog/catalog.state.js @@ -24,6 +24,7 @@ import brUtils from 'brooklyn-ui-utils/utils/general'; import template from './catalog.template.html'; import {analyzeDescription} from 'brooklyn-ui-utils/md-helper'; import {HIDE_INTERSTITIAL_SPINNER_EVENT} from 'brooklyn-ui-utils/interstitial-spinner/interstitial-spinner'; +import {CATALOG_UPLOAD_COMPLETED} from 'brooklyn-ui-utils/catalog-uploader/catalog-uploader'; const MODULE_NAME = 'catalog.state'; @@ -136,10 +137,15 @@ export function catalogController($scope, $rootScope, catalogApi, brUtilsGeneral $rootScope.$broadcast('open-catalog-uploader'); }; - catalogApi.getBundles({params: {detail: true}}).then(data => { - $scope.$emit(HIDE_INTERSTITIAL_SPINNER_EVENT); - - processBundles(data); + function getBundles(initialLoad) { + catalogApi.getBundles({params: {detail: true}}).then(data => { + if (initialLoad) $scope.$emit(HIDE_INTERSTITIAL_SPINNER_EVENT); + processBundles(data); + }); + } + getBundles(true); + $scope.$on(CATALOG_UPLOAD_COMPLETED, ()=>{ + getBundles(false); }); function processBundles(bundles) { diff --git a/ui-modules/utils/catalog-uploader/catalog-uploader.html b/ui-modules/utils/catalog-uploader/catalog-uploader.html index bc6b9d4f..0cb633e1 100644 --- a/ui-modules/utils/catalog-uploader/catalog-uploader.html +++ b/ui-modules/utils/catalog-uploader/catalog-uploader.html @@ -17,46 +17,70 @@ under the License. --> <div class="brooklyn-catalog-uploader"> - <i class="fa fa-times target-close pull-right" ng-click="close()"></i> - <div class="container"> - <div class="row"> - <div class="col-sm-8 text-center"> - <p><i class="fa fa-3x fa-upload"></i></p> - <p> - <input type="file" name="files" id="files" multiple custom-on-change="filesChanged" /> - <label for="files" ng-click="choose()"><strong>Choose files</strong><span class="drag-upload"> or drag & drop them here</span>.</label> - </p> - </div> + <div class="brooklyn-catalog-uploader-screen"> + </div> + <div class="brooklyn-catalog-uploader-inner"> + <div class="pull-right" style="position: absolute; right: 10px; top: 8px;"> + <i class="fa fa-times target-close" ng-click="close()"></i> + </div> + <div style="display: flex; flex-direction: column; height: 100%; width: 100%;"> + + <div style="flex: 0 1 auto; height: 60px;"></div> + + <div class="text-center"> + <p><i class="fa fa-3x fa-upload"></i></p> + <p> + <input type="file" name="files" id="files" multiple custom-on-change="filesChanged" /> + <label for="files" ng-click="choose()"><strong>Choose files</strong><span class="drag-upload"> or drag & drop them here</span>.</label> + </p> </div> - <div class="row"> - <div class="col-sm-8 text-muted upload-item" ng-repeat="selectedFile in selectedFiles" ng-init="isDetailsCollapsed = true"> - <div class="progress"> - <div class="progress-bar progress-bar-striped" role="progressbar" ng-class="{'active': !selectedFile.error && !selectedFile.result, 'progress-bar-danger': selectedFile.error, 'progress-bar-success': selectedFile.result}" style="width: 100%"> - <span ng-if="!selectedFile.result && !selectedFile.error">Importing</span> {{selectedFile.name}} - </div> - </div> - <p ng-click="isDetailsCollapsed = !isDetailsCollapsed" class="upload-item-details-link"><i class="fa fa-info-circle"></i> {{isDetailsCollapsed ? 'View' : 'hide'}} details</p> - <div uib-collapse="isDetailsCollapsed" class="upload-item-details"> - <p ng-if="selectedFile.result">Successfully imported the following items: - <ul class="fa-ul"> - <li ng-repeat="(key, item) in selectedFile.result track by key"> - <i class="fa-li fa fa-check-square"></i> - <a ng-href="{{getCatalogItemUrl(item)}}" target="_blank">{{item.itemType}} {{item.symbolicName}}:{{item.version}}</a> - </li> - </ul> - </p> - <p ng-if="selectedFile.error">{{selectedFile.error}}</p> + <div style="flex: 0 1 auto; height: 15px;"></div> + + <div class="upload-item" ng-repeat="selectedFile in selectedFiles" ng-init="isDetailsCollapsed = true"> + <div class="progress"> + <div class="progress-bar" role="progressbar" ng-class="{ + 'active': !selectedFile.error && !selectedFile.result, + 'progress-bar-striped': !selectedFile.error && !selectedFile.result, + 'progress-bar-danger': selectedFile.error, 'progress-bar-success': selectedFile.result}" style="width: 100%"> + <span ng-if="!selectedFile.result && !selectedFile.error">Importing</span> {{selectedFile.name}} </div> </div> - </div> - <div class="row"> - <div class="col-sm-8 text-left"> - <button class="btn btn-success" ng-click="close()"> - <i class="fa fa-fw fa-arrow-circle-left"></i> - Return to catalog - </button> + + <span class="upload-item-details-link" ng-click="isDetailsCollapsed = !isDetailsCollapsed"> + <span ng-if="selectedFile.error" style="color: darkred;"> + <i class="fa fa-times"></i> Upload failed. + </span> + <span ng-if="!selectedFile.error && selectedFile.result"> + <i class="fa fa-check"></i> <b>Completed.</b> + </span> + <span ng-if="selectedFile.error || selectedFile.result"> + Click {{isDetailsCollapsed ? 'for' : 'to hide'}} {{selectedFile.error ? ' error ' : ''}} details. + </span> + </span> + + <div uib-collapse="isDetailsCollapsed" class="upload-item-details"> + <p ng-if="selectedFile.result">Successfully imported the following items: + <ul class="fa-ul"> + <li ng-repeat="(key, item) in selectedFile.result track by key"> + <i class="fa-li fa fa-check-square"></i> + <a ng-href="{{getCatalogItemUrl(item)}}" target="_blank">{{item.itemType}} {{item.symbolicName}}:{{item.version}}</a> + </li> + </ul> + </p> + <p ng-if="selectedFile.error">{{selectedFile.error}}</p> </div> </div> + + <div style="flex: 1 1 auto; height: 15px;"></div> + + <div class="text-right" style="flex: 0 0 auto;"> + <button class="btn btn-success" ng-click="close()"> + <i class="fa fa-fw fa-arrow-circle-left"></i> + Return to catalog + </button> + </div> + <div style="flex: 0 0 auto; height: 10px;"></div> </div> + </div> </div> diff --git a/ui-modules/utils/catalog-uploader/catalog-uploader.js b/ui-modules/utils/catalog-uploader/catalog-uploader.js index d16f7716..0beed85b 100644 --- a/ui-modules/utils/catalog-uploader/catalog-uploader.js +++ b/ui-modules/utils/catalog-uploader/catalog-uploader.js @@ -34,10 +34,12 @@ const MODULE_NAME = 'brooklyn.components.catalog-uploader'; angular.module(MODULE_NAME, [catalogApi]) .service('brooklynCatalogUploader', ['$q', 'catalogApi', catalogUploaderService]) .directive('customOnChange', customOnChangeDirective) - .directive('brooklynCatalogUploader', ['$compile', 'brooklynCatalogUploader', catalogUploaderDirective]); + .directive('brooklynCatalogUploader', ['$compile', '$rootScope', 'brooklynCatalogUploader', catalogUploaderDirective]); export default MODULE_NAME; +export const CATALOG_UPLOAD_COMPLETED = "brooklyn-catalog-upload-completed"; + /** * @ngdoc directive * @name brooklynCatalogUploader @@ -52,7 +54,7 @@ export default MODULE_NAME; * @param {string} brooklynCatalogUploader The value can be empty. Otherwise, the directive will listen for any event broadcasted * with this name and will trigger the overlay upon reception. */ -export function catalogUploaderDirective($compile, brooklynCatalogUploader) { +export function catalogUploaderDirective($compile, $rootScope, brooklynCatalogUploader) { return { restrict: 'A', link: link @@ -67,27 +69,32 @@ export function catalogUploaderDirective($compile, brooklynCatalogUploader) { element.append($compile(template)(scope)); let counter = 0; + let requireManualClose = false; element.bind('drag dragstart dragend dragover dragenter dragleave drop', (event)=> { event.preventDefault(); event.stopPropagation(); }).bind('drag dragstart dragover dragenter', (event)=> { event.dataTransfer.dropEffect = 'copy'; element.addClass('br-drag-active'); + element.addClass('br-drag-active-2'); }).bind('dragenter', ()=> { counter++; }).bind('dragleave', (event)=> { counter--; - if (counter === 0) { - element.removeClass('br-drag-active'); - } + element.removeClass('br-drag-active-2'); + if (!requireManualClose && counter === 0) element.removeClass('br-drag-active'); // close if we were triggered by a drag }).bind('drop', (event)=> { scope.upload(event.dataTransfer.files); + counter--; + element.removeClass('br-drag-active-2'); + requireManualClose = true; + if (!requireManualClose && counter === 0) element.removeClass('br-drag-active'); // close if we were triggered by a drag }); let field = attrs.brooklynCatalogUploader; if (angular.isDefined(field)) { scope.$on(field, ()=> { - counter++; + requireManualClose = true; element.addClass('br-drag-active'); }); } @@ -95,7 +102,8 @@ export function catalogUploaderDirective($compile, brooklynCatalogUploader) { scope.selectedFiles = []; scope.close = ()=> { - counter--; + requireManualClose = false; + counter = 0; scope.selectedFiles = []; // clean up the imported file list on returning to catalog, still needs a manual refresh to show the imported bundle element.removeClass('br-drag-active'); }; @@ -110,7 +118,9 @@ export function catalogUploaderDirective($compile, brooklynCatalogUploader) { brooklynCatalogUploader.upload(file).then((data)=> { file.result = data; + $rootScope.$broadcast(CATALOG_UPLOAD_COMPLETED); }).catch((error)=> { + console.warn("ERROR uploading "+file, error); file.error = error; }).finally(()=> { scope.$applyAsync(); diff --git a/ui-modules/utils/catalog-uploader/catalog-uploader.less b/ui-modules/utils/catalog-uploader/catalog-uploader.less index b2e174c2..7cc106d5 100644 --- a/ui-modules/utils/catalog-uploader/catalog-uploader.less +++ b/ui-modules/utils/catalog-uploader/catalog-uploader.less @@ -17,50 +17,77 @@ * under the License. */ .brooklyn-catalog-uploader { - padding: 50px; - z-index: 100; visibility: hidden; - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: white; - overflow-y: scroll; - transition: all 0.3s ease; opacity: 0; - .upload-box { - background: @gray-lighter; - border: 5px @gray-light dashed; - } + z-index: 100; + width: 100%; + height: 100%; - .target-close { - cursor: pointer; + &, .brooklyn-catalog-uploader-screen, .brooklyn-catalog-uploader-inner { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; } - input[type=file] { - display: none; + .brooklyn-catalog-uploader-screen { + width: 100%; + height: 100%; + background-color: black; + opacity: 60%; + z-index: 50; } + .brooklyn-catalog-uploader-inner { + padding: 50px; + z-index: 100; + width: 70%; + height: 60%; + margin: auto; + margin-top: 10%; - label > strong { - text-decoration: underline; - cursor: pointer; - color: @brand-primary; - } + border: 3px @brand-primary solid; + border-radius: 6px; + + background-color: white; + overflow-y: scroll; + opacity: 1; - .upload-item { - margin-bottom: 1em; + .upload-box { + background: @gray-lighter; + border: 5px @gray-light dashed; + } - .progress { - margin-bottom: 0; + .target-close { + cursor: pointer; + } - .progress-bar { - transition: background-color 0.3s ease; - } + input[type=file] { + display: none; } - .upload-item-details-link { + + label > strong { + text-decoration: underline; cursor: pointer; + color: @brand-primary; + } + + .upload-item { + margin-bottom: 1em; + color: @gray-light; + + .progress { + margin-bottom: 0; + + .progress-bar { + transition: background-color 0.3s ease; + } + } + + .upload-item-details-link { + cursor: pointer; + } } } } @@ -68,5 +95,11 @@ .br-drag-active .brooklyn-catalog-uploader { visibility: visible; opacity: 1; + transition: 0.3s ease; } +.br-drag-active-2 .brooklyn-catalog-uploader { + .brooklyn-catalog-uploader-inner { + border: 3px @brand-primary dashed !important; + } +}
