From 0db8c28bd57b7cad7deb8d96e08c618dd34b8692 Mon Sep 17 00:00:00 2001 From: Felix Niklas Date: Tue, 6 Nov 2018 22:24:29 +0100 Subject: [PATCH] Some search enhancements and fixes. --- .../app/controllers/navigation.coffee | 62 +++++++------- .../app/lib/app_post/global_search.coffee | 6 +- .../javascripts/app/views/navigation.jst.eco | 1 + .../app/views/navigation/no_result.jst.eco | 4 + .../app/views/popover/ticket.jst.eco | 2 +- app/assets/stylesheets/svg-dimensions.css | 2 + app/assets/stylesheets/zammad.scss | 80 ++++++++++++++++++- public/assets/images/icons.svg | 19 +++++ public/assets/images/icons/mood-sad.svg | 10 +++ public/assets/images/icons/spinner-small.svg | 16 ++++ 10 files changed, 167 insertions(+), 35 deletions(-) create mode 100644 app/assets/javascripts/app/views/navigation/no_result.jst.eco create mode 100644 public/assets/images/icons/mood-sad.svg create mode 100644 public/assets/images/icons/spinner-small.svg diff --git a/app/assets/javascripts/app/controllers/navigation.coffee b/app/assets/javascripts/app/controllers/navigation.coffee index d3b0efbe6..45078c8ba 100644 --- a/app/assets/javascripts/app/controllers/navigation.coffee +++ b/app/assets/javascripts/app/controllers/navigation.coffee @@ -18,7 +18,7 @@ class App.Navigation extends App.ControllerWidgetPermanent 'focus #global-search': 'searchFocus' 'blur #global-search': 'searchBlur' 'keyup #global-search': 'listNavigate' - 'click .js-global-search-result': 'andClose' + 'click .js-global-search-result': 'emptyAndClose' 'click .js-details-link': 'openExtendedSearch' 'change .js-menu .js-switch input': 'switch' @@ -159,25 +159,26 @@ class App.Navigation extends App.ControllerWidgetPermanent type: 'personal' ) - renderResult: (result = []) => + renderResult: (result = [], noChange) => + if noChange + return + @removePopovers() # remove result if not result exists if _.isEmpty(result) - @searchContainer.removeClass('open') - @globalSearch.close() - @searchResult.html('') + @searchContainer.removeClass('loading').addClass('no-match') + @searchResult.html(App.view('navigation/no_result')()) return + @searchContainer.removeClass('no-match loading') + # build markup html = App.view('navigation/result')( result: result ) @searchResult.html(html) - # show result list - @searchContainer.addClass('open') - @renderPopovers() render: -> @@ -202,26 +203,22 @@ class App.Navigation extends App.ControllerWidgetPermanent searchFocus: (e) => @query = '' # reset query cache - @searchContainer.addClass('focused') App.PopoverProvidable.anyPopoversDestroy() - @search() + @searchContainer.addClass('focused') searchBlur: (e) => - # delay to be able to click x update = => query = @searchInput.val().trim() if !query @emptyAndClose() return - @searchContainer.removeClass('focused') @delay(update, 100, 'removeFocused') listNavigate: (e) => if e.keyCode is 27 # close on esc @emptyAndClose() - @searchInput.blur() return else if e.keyCode is 38 # up @nudge(e, -1) @@ -230,14 +227,13 @@ class App.Navigation extends App.ControllerWidgetPermanent @nudge(e, 1) return else if e.keyCode is 13 # enter - if @$('.global-search-menu .js-details-link.is-hover').get(0) - @openExtendedSearch() - return - href = @$('.global-search-result .nav-tab.is-hover').attr('href') - return if !href - @navigate(href) - @emptyAndClose() @searchInput.blur() + href = @$('.global-search-result .nav-tab.is-hover').attr('href') + if href + @navigate(href) + else + @openExtendedSearch() + @emptyAndClose() return # on other keys, show result @@ -281,25 +277,33 @@ class App.Navigation extends App.ControllerWidgetPermanent @scrollToIfNeeded(prev, false) emptyAndClose: => + @query = '' @searchInput.val('') - @searchContainer.removeClass('filled').removeClass('open').removeClass('focused') + @searchContainer.removeClass('focused filled open no-match loading') @globalSearch.close() - @delayedRemoveAnyPopover() andClose: => + @query = '' @searchInput.blur() - @searchContainer.removeClass('open') + @searchContainer.removeClass('open no-match loading') @globalSearch.close() - @delayedRemoveAnyPopover() search: => query = @searchInput.val().trim() - return if !query - return if query is @query + @searchContainer.toggleClass('filled', !!query) + + # if we started a new search and already typed something in + if query != '' and @query == '' + @searchContainer.addClass('open no-match loading') + @query = query - @searchContainer.toggleClass('filled', !!@query) + + if @query == '' + @searchContainer.removeClass('open') + return + @globalSearch.search(query: @query) filterNavbar: (values, user, parent = null) -> @@ -399,11 +403,11 @@ class App.Navigation extends App.ControllerWidgetPermanent url = params.url type = params.type if type is 'menu' - @$('.js-menu .is-active, .js-details-link.is-active').removeClass('is-active') + @$('.js-menu .is-active').removeClass('is-active') else @$('.is-active').removeClass('is-active') return if !url || url is '#' - @$("[href=\"#{url}\"]").addClass('is-active') + @$(".js-menu [href=\"#{url}\"], .tasks [href=\"#{url}\"]").addClass('is-active') recentViewNavbarItemsRebuild: => diff --git a/app/assets/javascripts/app/lib/app_post/global_search.coffee b/app/assets/javascripts/app/lib/app_post/global_search.coffee index 2724624fe..1e115fdf1 100644 --- a/app/assets/javascripts/app/lib/app_post/global_search.coffee +++ b/app/assets/javascripts/app/lib/app_post/global_search.coffee @@ -45,10 +45,12 @@ class App.GlobalSearch extends App.Controller renderTry: (result, query) => # if result hasn't changed, do not rerender - diff = false if @lastQuery is query && @searchResultCache[query] diff = difference(@searchResultCache[query].result, result) - return if diff isnt false && _.isEmpty(diff) + if _.isEmpty(diff) + @render(result, true) + return + @lastQuery = query # cache search result diff --git a/app/assets/javascripts/app/views/navigation.jst.eco b/app/assets/javascripts/app/views/navigation.jst.eco index 3ff4a2713..a4ee50396 100644 --- a/app/assets/javascripts/app/views/navigation.jst.eco +++ b/app/assets/javascripts/app/views/navigation.jst.eco @@ -2,6 +2,7 @@
<%- @Icon('magnifier') %> +
<%- @Icon('spinner-small') %>
diff --git a/app/assets/javascripts/app/views/navigation/no_result.jst.eco b/app/assets/javascripts/app/views/navigation/no_result.jst.eco new file mode 100644 index 000000000..b335db81a --- /dev/null +++ b/app/assets/javascripts/app/views/navigation/no_result.jst.eco @@ -0,0 +1,4 @@ + \ No newline at end of file diff --git a/app/assets/javascripts/app/views/popover/ticket.jst.eco b/app/assets/javascripts/app/views/popover/ticket.jst.eco index b124eb3b7..f012c73a4 100644 --- a/app/assets/javascripts/app/views/popover/ticket.jst.eco +++ b/app/assets/javascripts/app/views/popover/ticket.jst.eco @@ -1,4 +1,4 @@ -
+
<%- @Icon(@object.icon(), @object.iconClass()) %> <%- @object.iconTitle() %>

diff --git a/app/assets/stylesheets/svg-dimensions.css b/app/assets/stylesheets/svg-dimensions.css index 0273b7a2d..24e902dd3 100644 --- a/app/assets/stylesheets/svg-dimensions.css +++ b/app/assets/stylesheets/svg-dimensions.css @@ -66,6 +66,7 @@ .icon-mood-bad { width: 60px; height: 59px; } .icon-mood-good { width: 60px; height: 59px; } .icon-mood-ok { width: 60px; height: 59px; } +.icon-mood-sad { width: 60px; height: 59px; } .icon-mood-superbad { width: 60px; height: 59px; } .icon-mood-supergood { width: 60px; height: 59px; } .icon-mute { width: 16px; height: 16px; } @@ -97,6 +98,7 @@ .icon-signout { width: 15px; height: 19px; } .icon-small-dot { width: 16px; height: 16px; } .icon-sms { width: 17px; height: 17px; } +.icon-spinner-small { width: 15px; height: 15px; } .icon-split { width: 16px; height: 17px; } .icon-status-modified-outer-circle { width: 16px; height: 16px; } .icon-status { width: 16px; height: 16px; } diff --git a/app/assets/stylesheets/zammad.scss b/app/assets/stylesheets/zammad.scss index 8972b4666..c31177b3a 100644 --- a/app/assets/stylesheets/zammad.scss +++ b/app/assets/stylesheets/zammad.scss @@ -2770,6 +2770,20 @@ ol.tabs li { } } +.icon-spinner-medium { + animation: spinIcon .9s steps(12) infinite; +} + +.icon-spinner-small { + animation: spinIcon .9s steps(8) infinite; +} + +@keyframes spinIcon { + to { + transform: rotate(-1turn); + } +} + .color-swatch { padding: 2px; margin: -2px 0 -4px; @@ -3424,12 +3438,25 @@ footer { fill: $task-state-pending-color; } +.state-badge { + display: flex; + align-items: center; + + .icon-task-state { + margin-right: 2px; + margin-top: 0; + display: inline; + vertical-align: middle; + } +} + .search { padding: 11px 5px 4px 10px; @include rtl(padding, 11px 10px 4px 0px); border-bottom: 1px solid rgba(240,250,255,.05); flex-shrink: 0; display: flex; + align-items: flex-start; background-color: inherit; } @@ -3437,7 +3464,8 @@ footer { flex: 1; border-radius: 15px; position: relative; - transition: 240ms; + transition: margin-right 120ms; + will-change: margin-right; } .empty-search { @@ -3483,18 +3511,46 @@ footer { -webkit-appearance: none; } + .search .search-loader { + position: absolute; + top: 8px; + left: 10px; + z-index: 2; + opacity: 0; + transition: 0s; + transition: .1s 0s; + + .icon { + fill: white; + opacity: 0.5; + } + } + + .search.loading .search-loader { + opacity: 1; + transition: .2s .5s; + } + .search .icon-magnifier { position: absolute; top: 8px; left: 10px; @include bidi-style(left, 10px, right, auto); z-index: 2; - opacity: 0.5; + opacity: .5; fill: white; + transition: 0s; + transition: .1s 0s; + } + + .search.loading .icon-magnifier { + opacity: 0; + transition: .2s .5s; } .search.focused .search-holder { - @include bidi-style(margin-right, -46px, margin-left, 0); + transition: margin-right 240ms; + @include bidi-style(margin-right, -59px, margin-left, 0); } .search.focused .logo { @@ -3560,6 +3616,10 @@ footer { padding: 9px 15px 8px 0; margin-bottom: 7px; height: auto !important; + + .no-match & { + display: none; + } .nav-tab-icon { width: 18px; @@ -3587,6 +3647,15 @@ footer { list-style: none; } + .global-search-detail-no-result { + margin: 0 10px; + + .icon { + width: 30px; + height: 29px; + } + } + .user-menu { padding: 0; margin: 0; @@ -5848,6 +5917,11 @@ footer { border-radius: 3px; color: white; border: none; + + .icon { + margin-right: 10px; + fill: currentColor; + } &.alert--info { background: hsl(203,65%,55%); diff --git a/public/assets/images/icons.svg b/public/assets/images/icons.svg index 4e8001847..a5293382d 100644 --- a/public/assets/images/icons.svg +++ b/public/assets/images/icons.svg @@ -460,6 +460,11 @@ mood-ok + + + mood-sad + + mood-superbad @@ -649,6 +654,20 @@ sms + + + spinner-small + + + + + + + + + + + split diff --git a/public/assets/images/icons/mood-sad.svg b/public/assets/images/icons/mood-sad.svg new file mode 100644 index 000000000..17e9976c3 --- /dev/null +++ b/public/assets/images/icons/mood-sad.svg @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg width="60px" height="59px" viewBox="0 0 60 59" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> + <!-- Generator: Sketch 51.1 (57501) - http://www.bohemiancoding.com/sketch --> + <title>mood-sad + Created with Sketch. + + + + + \ No newline at end of file diff --git a/public/assets/images/icons/spinner-small.svg b/public/assets/images/icons/spinner-small.svg new file mode 100644 index 000000000..4c3a2a74f --- /dev/null +++ b/public/assets/images/icons/spinner-small.svg @@ -0,0 +1,16 @@ + + + + spinner-small + Created with Sketch. + + + + + + + + + + + \ No newline at end of file