From 7c14bed4873b92124bcbe422fed918836b8f5df5 Mon Sep 17 00:00:00 2001 From: kingcody Date: Fri, 1 Aug 2014 06:40:00 -0400 Subject: [PATCH] feat(uibootstrap-modal): add basic modal service and template when using uibootstrap Changes: (only if ui-bootstrap is selected) - add `client/components/modal` folder - modal folder contains service, markup template, and stylesheet - modal service is intended to be extended, comes with `Modal.confirm.delete()` method - admin and main page will both use `Modal.confirm.delete()` Todo: - review code for cleanliness and correctness - possibly extend the modal service to include a basic alert class? - write test for `Modal` service? --- .../client/app/admin(auth)/admin(html).html | 2 +- .../client/app/admin(auth)/admin(jade).jade | 2 +- .../admin.controller(coffee).coffee | 4 +- .../app/admin(auth)/admin.controller(js).js | 6 +- app/templates/client/app/main/main(html).html | 2 +- app/templates/client/app/main/main(jade).jade | 2 +- .../app/main/main.controller(coffee).coffee | 4 +- .../client/app/main/main.controller(js).js | 6 +- .../modal(uibootstrap)/modal(css).css | 23 ++++++ .../modal(uibootstrap)/modal(html).html | 11 +++ .../modal(uibootstrap)/modal(jade).jade | 8 ++ .../modal(uibootstrap)/modal(less).less | 25 ++++++ .../modal(uibootstrap)/modal(sass).scss | 25 ++++++ .../modal(uibootstrap)/modal(stylus).styl | 23 ++++++ .../modal.service(coffee).coffee | 71 +++++++++++++++++ .../modal(uibootstrap)/modal.service(js).js | 77 +++++++++++++++++++ 16 files changed, 277 insertions(+), 14 deletions(-) create mode 100644 app/templates/client/components/modal(uibootstrap)/modal(css).css create mode 100644 app/templates/client/components/modal(uibootstrap)/modal(html).html create mode 100644 app/templates/client/components/modal(uibootstrap)/modal(jade).jade create mode 100644 app/templates/client/components/modal(uibootstrap)/modal(less).less create mode 100644 app/templates/client/components/modal(uibootstrap)/modal(sass).scss create mode 100644 app/templates/client/components/modal(uibootstrap)/modal(stylus).styl create mode 100644 app/templates/client/components/modal(uibootstrap)/modal.service(coffee).coffee create mode 100644 app/templates/client/components/modal(uibootstrap)/modal.service(js).js diff --git a/app/templates/client/app/admin(auth)/admin(html).html b/app/templates/client/app/admin(auth)/admin(html).html index 5c27c7af2..918b0c5be 100644 --- a/app/templates/client/app/admin(auth)/admin(html).html +++ b/app/templates/client/app/admin(auth)/admin(html).html @@ -6,7 +6,7 @@
  • {{user.name}}
    {{user.email}} - +
  • \ No newline at end of file diff --git a/app/templates/client/app/admin(auth)/admin(jade).jade b/app/templates/client/app/admin(auth)/admin(jade).jade index a4672dadd..3c4a0e525 100644 --- a/app/templates/client/app/admin(auth)/admin(jade).jade +++ b/app/templates/client/app/admin(auth)/admin(jade).jade @@ -7,5 +7,5 @@ div(ng-include='"components/navbar/navbar.html"') strong {{user.name}} br span.text-muted {{user.email}} - a.trash(ng-click='delete(user)') + a.trash(ng-click='delete(<% if(filters.uibootstrap) { %>user.name + " (" + user.email + ")", <% } %>user)') span.glyphicon.glyphicon-trash.pull-right diff --git a/app/templates/client/app/admin(auth)/admin.controller(coffee).coffee b/app/templates/client/app/admin(auth)/admin.controller(coffee).coffee index 1e3f7988f..aa9a1b025 100644 --- a/app/templates/client/app/admin(auth)/admin.controller(coffee).coffee +++ b/app/templates/client/app/admin(auth)/admin.controller(coffee).coffee @@ -1,12 +1,12 @@ 'use strict' angular.module '<%= scriptAppName %>' -.controller 'AdminCtrl', ($scope, $http, Auth, User) -> +.controller 'AdminCtrl', ($scope, $http, Auth, User<% if(filters.uibootstrap) { %>, Modal<% } %>) -> # Use the User $resource to fetch all users $scope.users = User.query() - $scope.delete = (user) -> + $scope.delete = <% if(filters.uibootstrap) { %>Modal.confirm.delete <% } %>(user) -> User.remove id: user._id angular.forEach $scope.users, (u, i) -> $scope.users.splice i, 1 if u is user diff --git a/app/templates/client/app/admin(auth)/admin.controller(js).js b/app/templates/client/app/admin(auth)/admin.controller(js).js index dd6b09405..3240777e6 100644 --- a/app/templates/client/app/admin(auth)/admin.controller(js).js +++ b/app/templates/client/app/admin(auth)/admin.controller(js).js @@ -1,17 +1,17 @@ 'use strict'; angular.module('<%= scriptAppName %>') - .controller('AdminCtrl', function ($scope, $http, Auth, User) { + .controller('AdminCtrl', function ($scope, $http, Auth, User<% if(filters.uibootstrap) { %>, Modal<% } %>) { // Use the User $resource to fetch all users $scope.users = User.query(); - $scope.delete = function(user) { + $scope.delete = <% if(filters.uibootstrap) { %>Modal.confirm.delete(<% } %>function(user) { User.remove({ id: user._id }); angular.forEach($scope.users, function(u, i) { if (u === user) { $scope.users.splice(i, 1); } }); - }; + }<% if(filters.uibootstrap) { %>)<% } %>; }); diff --git a/app/templates/client/app/main/main(html).html b/app/templates/client/app/main/main(html).html index 3d6fa2019..12e3f40ac 100644 --- a/app/templates/client/app/main/main(html).html +++ b/app/templates/client/app/main/main(html).html @@ -13,7 +13,7 @@

    'Allo, 'Allo!

    Features:

    <% if(filters.socketio) { %> diff --git a/app/templates/client/app/main/main(jade).jade b/app/templates/client/app/main/main(jade).jade index 25915fb0f..fcb921b97 100644 --- a/app/templates/client/app/main/main(jade).jade +++ b/app/templates/client/app/main/main(jade).jade @@ -14,7 +14,7 @@ header#banner.hero-unit li a(href='#', tooltip='{{thing.info}}') | {{thing.name}}<% if(filters.socketio) { %> - button.close(type='button', ng-click='deleteThing(thing)') ×<% } %><% if(filters.socketio) { %> + button.close(type='button', ng-click='deleteThing(<% if(filters.uibootstrap) { %>thing.name, <% } %>thing)') ×<% } %><% if(filters.socketio) { %> form.thing-form label Syncs in realtime across clients diff --git a/app/templates/client/app/main/main.controller(coffee).coffee b/app/templates/client/app/main/main.controller(coffee).coffee index 29c72dec3..e4476e868 100644 --- a/app/templates/client/app/main/main.controller(coffee).coffee +++ b/app/templates/client/app/main/main.controller(coffee).coffee @@ -1,7 +1,7 @@ 'use strict' angular.module '<%= scriptAppName %>' -.controller 'MainCtrl', ($scope, $http<% if(filters.socketio) { %>, socket<% } %>) -> +.controller 'MainCtrl', ($scope, $http<% if(filters.socketio) { %>, socket<% } %><% if(filters.uibootstrap && filters.mongoose) { %>, Modal<% } %>) -> $scope.awesomeThings = [] $http.get('/api/things').success (awesomeThings) -> @@ -15,7 +15,7 @@ angular.module '<%= scriptAppName %>' $scope.newThing = '' - $scope.deleteThing = (thing) -> + $scope.deleteThing = <% if(filters.uibootstrap) { %>Modal.confirm.delete <% } %>(thing) -> $http.delete '/api/things/' + thing._id<% } %><% if(filters.socketio) { %> $scope.$on '$destroy', -> diff --git a/app/templates/client/app/main/main.controller(js).js b/app/templates/client/app/main/main.controller(js).js index a65590465..3b3b4871e 100644 --- a/app/templates/client/app/main/main.controller(js).js +++ b/app/templates/client/app/main/main.controller(js).js @@ -1,7 +1,7 @@ 'use strict'; angular.module('<%= scriptAppName %>') - .controller('MainCtrl', function ($scope, $http<% if(filters.socketio) { %>, socket<% } %>) { + .controller('MainCtrl', function ($scope, $http<% if(filters.socketio) { %>, socket<% } %><% if(filters.uibootstrap && filters.mongoose) { %>, Modal<% } %>) { $scope.awesomeThings = []; $http.get('/api/things').success(function(awesomeThings) { @@ -17,9 +17,9 @@ angular.module('<%= scriptAppName %>') $scope.newThing = ''; }; - $scope.deleteThing = function(thing) { + $scope.deleteThing = <% if(filters.uibootstrap) { %>Modal.confirm.delete(<% } %>function(thing) { $http.delete('/api/things/' + thing._id); - };<% } %><% if(filters.socketio) { %> + }<% if(filters.uibootstrap) { %>)<% } %>;<% } %><% if(filters.socketio) { %> $scope.$on('$destroy', function () { socket.unsyncUpdates('thing'); diff --git a/app/templates/client/components/modal(uibootstrap)/modal(css).css b/app/templates/client/components/modal(uibootstrap)/modal(css).css new file mode 100644 index 000000000..f5cc0d9e7 --- /dev/null +++ b/app/templates/client/components/modal(uibootstrap)/modal(css).css @@ -0,0 +1,23 @@ +.modal-primary .modal-header, +.modal-info .modal-header, +.modal-success .modal-header, +.modal-warning .modal-header, +.modal-danger .modal-header { + color: #fff; + border-radius: 5px 5px 0 0; +} +.modal-primary .modal-header { + background: #428bca; +} +.modal-info .modal-header { + background: #5bc0de; +} +.modal-success .modal-header { + background: #5cb85c; +} +.modal-warning .modal-header { + background: #f0ad4e; +} +.modal-danger .modal-header { + background: #d9534f; +} \ No newline at end of file diff --git a/app/templates/client/components/modal(uibootstrap)/modal(html).html b/app/templates/client/components/modal(uibootstrap)/modal(html).html new file mode 100644 index 000000000..4580254ff --- /dev/null +++ b/app/templates/client/components/modal(uibootstrap)/modal(html).html @@ -0,0 +1,11 @@ + + + \ No newline at end of file diff --git a/app/templates/client/components/modal(uibootstrap)/modal(jade).jade b/app/templates/client/components/modal(uibootstrap)/modal(jade).jade new file mode 100644 index 000000000..71b4321b3 --- /dev/null +++ b/app/templates/client/components/modal(uibootstrap)/modal(jade).jade @@ -0,0 +1,8 @@ +.modal-header + button.close(ng-if='modal.dismissable', type='button', ng-click='$dismiss()') × + h4.modal-title(ng-if='modal.title', ng-bind='modal.title') +.modal-body + p(ng-if='modal.text', ng-bind='modal.text') + div(ng-if='modal.html', ng-bind-html='modal.html') +.modal-footer + button.btn(ng-repeat='button in modal.buttons', ng-class='button.classes', ng-click='button.click($event)', ng-bind='button.text') diff --git a/app/templates/client/components/modal(uibootstrap)/modal(less).less b/app/templates/client/components/modal(uibootstrap)/modal(less).less new file mode 100644 index 000000000..dd1357d2c --- /dev/null +++ b/app/templates/client/components/modal(uibootstrap)/modal(less).less @@ -0,0 +1,25 @@ +.modal-primary, +.modal-info, +.modal-success, +.modal-warning, +.modal-danger { + .modal-header { + color: #fff; + border-radius: 5px 5px 0 0; + } +} +.modal-primary .modal-header { + background: @brand-primary; +} +.modal-info .modal-header { + background: @brand-info; +} +.modal-success .modal-header { + background: @brand-success; +} +.modal-warning .modal-header { + background: @brand-warning; +} +.modal-danger .modal-header { + background: @brand-danger; +} diff --git a/app/templates/client/components/modal(uibootstrap)/modal(sass).scss b/app/templates/client/components/modal(uibootstrap)/modal(sass).scss new file mode 100644 index 000000000..3b0b9d96a --- /dev/null +++ b/app/templates/client/components/modal(uibootstrap)/modal(sass).scss @@ -0,0 +1,25 @@ +.modal-primary, +.modal-info, +.modal-success, +.modal-warning, +.modal-danger { + .modal-header { + color: #fff; + border-radius: 5px 5px 0 0; + } +} +.modal-primary .modal-header { + background: $brand-primary; +} +.modal-info .modal-header { + background: $brand-info; +} +.modal-success .modal-header { + background: $brand-success; +} +.modal-warning .modal-header { + background: $brand-warning; +} +.modal-danger .modal-header { + background: $brand-danger; +} diff --git a/app/templates/client/components/modal(uibootstrap)/modal(stylus).styl b/app/templates/client/components/modal(uibootstrap)/modal(stylus).styl new file mode 100644 index 000000000..d394ee047 --- /dev/null +++ b/app/templates/client/components/modal(uibootstrap)/modal(stylus).styl @@ -0,0 +1,23 @@ +.modal-primary +.modal-info +.modal-success +.modal-warning +.modal-danger + .modal-header + color #fff + border-radius 5px 5px 0 0 + +.modal-primary .modal-header + background #428bca + +.modal-info .modal-header + background #5bc0de + +.modal-success .modal-header + background #5cb85c + +.modal-warning .modal-header + background #f0ad4e + +.modal-danger .modal-header + background #d9534f diff --git a/app/templates/client/components/modal(uibootstrap)/modal.service(coffee).coffee b/app/templates/client/components/modal(uibootstrap)/modal.service(coffee).coffee new file mode 100644 index 000000000..eae01f69f --- /dev/null +++ b/app/templates/client/components/modal(uibootstrap)/modal.service(coffee).coffee @@ -0,0 +1,71 @@ +'use strict' + +angular.module '<%= scriptAppName %>' +.factory 'Modal', ($rootScope, $modal) -> + + ### + Opens a modal + @param {Object} scope - an object to be merged with modal's scope + @param {String} modalClass - (optional) class(es) to be applied to the modal + @return {Object} - the instance $modal.open() returns + ### + openModal = (scope, modalClass) -> + modalScope = $rootScope.$new() + scope = scope or {} + modalClass = modalClass or 'modal-default' + angular.extend modalScope, scope + $modal.open + templateUrl: 'components/modal/modal.html' + windowClass: modalClass + scope: modalScope + + + # Public API here + + # Confirmation modals + confirm: + + ### + Create a function to open a delete confirmation modal (ex. ng-click='myModalFn(name, arg1, arg2...)') + @param {Function} del - callback, ran when delete is confirmed + @return {Function} - the function to open the modal (ex. myModalFn) + ### + delete: (del) -> + del = del or angular.noop + + ### + Open a delete confirmation modal + @param {String} name - name or info to show on modal + @param {All} - any additional args are passed staight to del callback + ### + -> + args = Array::slice.call arguments + name = args.shift() + deleteModal = undefined + deleteModal = openModal( + modal: + dismissable: true + title: 'Confirm Delete' + html: '

    Are you sure you want to delete ' + name + ' ?

    ' + buttons: [ + { + classes: 'btn-danger' + text: 'Delete' + click: (e) -> + deleteModal.close e + return + } + { + classes: 'btn-default' + text: 'Cancel' + click: (e) -> + deleteModal.dismiss e + return + } + ] + , 'modal-danger') + deleteModal.result.then (event) -> + del.apply event, args + return + + return diff --git a/app/templates/client/components/modal(uibootstrap)/modal.service(js).js b/app/templates/client/components/modal(uibootstrap)/modal.service(js).js new file mode 100644 index 000000000..f01200174 --- /dev/null +++ b/app/templates/client/components/modal(uibootstrap)/modal.service(js).js @@ -0,0 +1,77 @@ +'use strict'; + +angular.module('<%= scriptAppName %>') + .factory('Modal', function ($rootScope, $modal) { + /** + * Opens a modal + * @param {Object} scope - an object to be merged with modal's scope + * @param {String} modalClass - (optional) class(es) to be applied to the modal + * @return {Object} - the instance $modal.open() returns + */ + function openModal(scope, modalClass) { + var modalScope = $rootScope.$new(); + scope = scope || {}; + modalClass = modalClass || 'modal-default'; + + angular.extend(modalScope, scope); + + return $modal.open({ + templateUrl: 'components/modal/modal.html', + windowClass: modalClass, + scope: modalScope + }); + } + + // Public API here + return { + + /* Confirmation modals */ + confirm: { + + /** + * Create a function to open a delete confirmation modal (ex. ng-click='myModalFn(name, arg1, arg2...)') + * @param {Function} del - callback, ran when delete is confirmed + * @return {Function} - the function to open the modal (ex. myModalFn) + */ + delete: function(del) { + del = del || angular.noop; + + /** + * Open a delete confirmation modal + * @param {String} name - name or info to show on modal + * @param {All} - any additional args are passed staight to del callback + */ + return function() { + var args = Array.prototype.slice.call(arguments), + name = args.shift(), + deleteModal; + + deleteModal = openModal({ + modal: { + dismissable: true, + title: 'Confirm Delete', + html: '

    Are you sure you want to delete ' + name + ' ?

    ', + buttons: [{ + classes: 'btn-danger', + text: 'Delete', + click: function(e) { + deleteModal.close(e); + } + }, { + classes: 'btn-default', + text: 'Cancel', + click: function(e) { + deleteModal.dismiss(e); + } + }] + } + }, 'modal-danger'); + + deleteModal.result.then(function(event) { + del.apply(event, args); + }); + }; + } + } + }; + });