nitweb: rewrite search ui
authorAlexandre Terrasa <alexandre@moz-code.org>
Wed, 16 Aug 2017 23:18:55 +0000 (19:18 -0400)
committerAlexandre Terrasa <alexandre@moz-code.org>
Tue, 26 Sep 2017 15:10:05 +0000 (11:10 -0400)
Display a new menu that allows the user to jump on a result page.

Signed-off-by: Alexandre Terrasa <alexandre@moz-code.org>

share/nitweb/directives/search/card.html [deleted file]
share/nitweb/directives/search/field.html [deleted file]
share/nitweb/directives/ui/search-field.html
share/nitweb/index.html
share/nitweb/javascripts/entities.js
share/nitweb/javascripts/ui.js
share/nitweb/stylesheets/nitweb.css
share/nitweb/stylesheets/search.css
share/nitweb/views/search.html [new file with mode: 0644]

diff --git a/share/nitweb/directives/search/card.html b/share/nitweb/directives/search/card.html
deleted file mode 100644 (file)
index 7052c60..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-<div class='card card-search'>
-       <div class='card-left text-center'>
-               <entity-tag mentity='mentity' />
-       </div>
-       <div class='card-body'>
-               <h5 class='card-heading'>
-                       <entity-signature mentity='mentity'/>
-                       <br>
-                       <small><entity-namespace namespace='mentity.namespace' /></small>
-               </h5>
-               <span class='synopsis' ng-bind-html='mentity.mdoc.html_synopsis' />
-       </div>
-</div>
diff --git a/share/nitweb/directives/search/field.html b/share/nitweb/directives/search/field.html
deleted file mode 100644 (file)
index d9ee5f1..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-<form>
-       <div class='form-group has-icon'>
-               <input placeholder='Search...' type='text' class='form-control search-input'
-                       ng-model-options='{ debounce: 300 }' ng-model='query'
-                       ng-keydown='update($event)' ng-change='search()'>
-               <span class='glyphicon glyphicon-search form-control-icon text-muted'></span>
-       </div>
-       <div ng-if='results.length > 0' class='search-results'>
-               <div class='card-list'>
-                       <search-card ng-click='selectEnter()' ng-class='{active: activeItem == $index}' ng-mouseover='setActive($index)' mentity='mentity' ng-repeat='mentity in results' />
-               </div>
-       </div>
-</form>
index e0bd4fd..a4d06a6 100644 (file)
@@ -2,7 +2,15 @@
        <div class='form-group has-icon'>
                <input placeholder='Search...' type='text' class='form-control search-input'
                        ng-model-options='{ debounce: 300 }' ng-model='vm.query'
-                       ng-keydown='update($event)' ng-change='vm.search()'>
+                       ng-keydown='vm.update($event)' ng-change='vm.search()'>
                <span class='glyphicon glyphicon-search form-control-icon text-muted'></span>
        </div>
+       <div ng-if='vm.results.results.length > 0' class='card-list search-results'>
+               <entity-card ng-click='vm.selectEnter()' ng-class='{active: vm.activeItem == $index}' ng-mouseover='vm.setActive($index)' mentity='mentity' ng-repeat='mentity in vm.results.results' />
+               <div class='card' ng-click='vm.selectEnter()' ng-mouseover='vm.setActive(vm.results.results.length)' ng-class='{active: vm.activeItem == vm.results.results.length}'>
+                       <div class='card-body'>
+                               Show all {{vm.results.total}} results for <a>"{{vm.query}}"</a>
+                       </div>
+               </div>
+       </div>
 </form>
index 4ce9148..92eb64e 100644 (file)
@@ -47,7 +47,7 @@
                                        </div>
                                </div>
                                <div class='col-xs-7'>
-                                       <search-field />
+                                       <ui-search-field />
                                </div>
                                <div class='col-xs-2'>
                                        <user-menu />
index 71bf600..545e487 100644 (file)
                                                .success(cb)
                                                .error(cbErr);
                                },
-
-                               search: function(q, n, cb, cbErr) {
-                                       $http.get('/api/search?q=' + q + '&n=' + n)
+                               search: function(q, p, n, cb, cbErr) {
+                                       $http.get('/api/search?q=' + q + '&p=' + p + '&n=' + n)
                                                .success(cb)
                                                .error(cbErr);
                                }
index 7a2f062..5327782 100644 (file)
        angular
                .module('ui', [])
 
-               .controller('SearchCtrl', function(Model, $scope, $location, $document) {
-
-                       $scope.query = '';
-
-                       $scope.reset = function() {
-                               $scope.activeItem = 0;
-                               $scope.results = [];
-                       }
-
-                       $scope.update = function(e) {
-                               if(e.keyCode == 38) {
-                                       $scope.selectUp();
-                               } else if(e.keyCode == 40) {
-                                       $scope.selectDown();
-                               } else if(e.keyCode == 27) {
-                                       $scope.selectEscape();
-                               } else if(e.keyCode == 13) {
-                                       $scope.selectEnter();
-                               }
-                       }
+               /* Search */
+
+               .config(function($stateProvider, $locationProvider) {
+                       $stateProvider
+                               .state('search', {
+                                       url: '/search?q&p&n',
+                                       controller: 'SearchCtrl',
+                                       controllerAs: 'vm',
+                                       templateUrl: 'views/search.html',
+                                       resolve: {
+                                               entities: function(Model, $q, $stateParams, $state) {
+                                                       var d = $q.defer();
+                                                       var query = $stateParams.q;
+                                                       var page = $stateParams.p ? $stateParams.p : 1;
+                                                       var limit = $stateParams.n ? $stateParams.n : 10;
+                                                       Model.search(query, page, limit, d.resolve,
+                                                               function() {
+                                                                       $state.go('404', null, { location: false })
+                                                               });
+                                                       return d.promise;
+                                               }
+                                       }
+                               })
+               })
 
-                       $scope.selectUp = function() {
-                               if($scope.activeItem > 0) {
-                                       $scope.activeItem -= 1;
-                               }
-                       }
+               .controller('SearchCtrl', function($scope, $state, $stateParams, entities) {
+                       var vm = this;
+                       vm.entities = entities;
+                       vm.query = $stateParams.q;
 
-                       $scope.selectDown = function() {
-                               if($scope.activeItem < $scope.results.length - 1) {
-                                       $scope.activeItem += 1;
-                               }
-                       }
+                       $scope.$on('change-page', function(e, page, limit) {
+                               $state.go('search', {q: vm.query, p: page, l: limit});
+                       })
+               })
 
-                       $scope.selectEnter = function(e) {
-                               $location.url($scope.results[$scope.activeItem].web_url);
-                               $scope.reset();
-                       }
+               .directive('uiSearchField', function($document) {
+                       return {
+                               restrict: 'E',
+                               replace: true,
+                               controller: function($scope, $state, $stateParams, $location, Model) {
+                                       var vm = this;
+                                       vm.search = function() {
+                                               if(!vm.query) {
+                                                       vm.reset();
+                                                       return;
+                                               }
+                                               Model.search(vm.query, 1, 8,
+                                                       function(data) {
+                                                               vm.reset();
+                                                               vm.results = data;
+                                                       }, function(err) {
+                                                               vm.reset();
+                                                               vm.error = err;
+                                                       });
+                                       }
 
-                       $scope.selectEscape = function() {
-                               $scope.reset();
-                       }
+                                       vm.reset = function() {
+                                               vm.activeItem = -1;
+                                               vm.results = {
+                                                       results: []
+                                               };
+                                       }
 
-                       $scope.setActive = function(index) {
-                               $scope.activeItem = index;
-                       }
+                                       vm.update = function(e) {
+                                               if(e.keyCode == 38) {
+                                                       vm.selectUp();
+                                               } else if(e.keyCode == 40) {
+                                                       vm.selectDown();
+                                               } else if(e.keyCode == 27) {
+                                                       vm.selectEscape();
+                                               } else if(e.keyCode == 13) {
+                                                       vm.selectEnter();
+                                               }
+                                       }
 
-                       $scope.search = function() {
-                               if(!$scope.query) {
-                                       $scope.reset();
-                                       return;
-                               }
-                               Model.search($scope.query, 10,
-                                       function(data) {
-                                               $scope.reset();
-                                               $scope.results = data;
-                                       }, function(err) {
-                                               $scope.reset();
-                                               $scope.error = err;
-                                       });
-                       }
+                                       vm.selectUp = function() {
+                                               if(vm.activeItem >= 0) {
+                                                       vm.activeItem -= 1;
+                                               }
+                                       }
 
-                       $scope.reset();
-               })
+                                       vm.selectDown = function() {
+                                               if(vm.activeItem < vm.results.results.length) {
+                                                       vm.activeItem += 1;
+                                               }
+                                       }
 
-               .directive('searchField', function($document) {
-                       return {
-                               restrict: 'E',
-                               replace: true,
-                               controller: 'SearchCtrl',
-                               controllerAs: 'searchCtrl',
-                               templateUrl: '/directives/search/field.html',
-                               link: function ($scope, element, attrs) {
+                                       vm.selectEnter = function(e) {
+                                               if(vm.activeItem >= 0 && vm.activeItem < vm.results.results.length) {
+                                                       $location.url(vm.results.results[vm.activeItem].web_url);
+                                               } else {
+                                                       $state.go('search', {q: vm.query, p: 1});
+                                               }
+                                               vm.reset();
+                                       }
+
+                                       vm.selectEscape = function() {
+                                               vm.reset();
+                                       }
+
+                                       vm.setActive = function(index) {
+                                               vm.activeItem = index;
+                                       }
+
+                                       vm.reset();
+
+                                       $scope.$watch(function() {
+                                               return $stateParams.q;
+                                       }, function(q) {
+                                               if(q) vm.query = q;
+                                       });
+                               },
+                               controllerAs: 'vm',
+                               templateUrl: 'directives/ui/search-field.html',
+                               link: function ($scope, element, attrs, ctrl) {
                                        $document.bind('click', function (event) {
                                                var isChild = $(element).has(event.target).length > 0;
                                                var isSelf = element[0] == event.target;
                                                var isInside = isChild || isSelf;
                                                if (!isInside) {
-                                                       $scope.reset();
+                                                       ctrl.reset();
                                                        $scope.$apply();
                                                }
                                        });
                        };
                })
 
-               .directive('searchCard', function() {
-                       return {
-                               restrict: 'E',
-                               scope: {
-                                       mentity: '='
-                               },
-                               replace: true,
-                               templateUrl: '/directives/search/card.html'
-                       };
-               })
+               /* Filters */
 
                .directive('uiFilters', function() {
                        return {
index 864c296..ccfcdd0 100644 (file)
        background: #FF8100;
 }
 
+[ng-click] {
+       cursor: pointer;
+}
+
 /* Body */
 
 body {
@@ -127,28 +131,6 @@ entity-list:hover .btn-filter {
     pointer-events: none;
 }
 
-/* search */
-
-.search-input {
-       width: 100%;
-}
-
-.search-results {
-       position: absolute;
-       margin-top: 2px;
-       right: 15px;
-       left: 15px;
-}
-
-.search-results .card.active {
-       background: #eee;
-       border-color: #eee;
-}
-
-.card-search {
-       cursor: pointer;
-}
-
 /* navs */
 
 .nav-tabs li { cursor: pointer; }
index 2f6e2db..68516dc 100644 (file)
 .navbar-fixed-top *:-moz-placeholder { color: #fff; }
 .navbar-fixed-top *::-moz-placeholder { color: #fff; }
 .navbar-fixed-top *:-ms-input-placeholder { color: #fff; }
+
+.search-input {
+       width: 100%;
+}
+
+.card-list.search-results {
+       position: absolute;
+       margin-top: 2px;
+       right: 15px;
+       left: 15px;
+       box-shadow: 1px 1px 3px rgba(0,0,0,0.12), -1px -1px 3px rgba(0,0,0,.12);
+}
+
+.search-results .card.active {
+       background: #eee;
+       border-color: #eee;
+}
diff --git a/share/nitweb/views/search.html b/share/nitweb/views/search.html
new file mode 100644 (file)
index 0000000..6614e9c
--- /dev/null
@@ -0,0 +1,12 @@
+<div class='container'>
+       <h2>
+               {{vm.entities.total}} matches for
+               <a ui-sref='search({q: vm.query})'>{{vm.query}}</a>
+       </h2>
+       <div class='card-list'>
+               <entity-card mentity='mentity' ng-repeat='mentity in vm.entities.results' />
+       </div>
+       <div class='container text-center' ng-if='vm.entities'>
+               <ui-pagination pagination='vm.entities'/>
+       </div>
+</div>