Browse Source

Merge branch 'develop' of github.com:martini/zammad into develop

Martin Edenhofer 9 years ago
parent
commit
f9e46fdae5

+ 141 - 43
app/assets/javascripts/app/controllers/layout_ref.js.coffee

@@ -938,21 +938,34 @@ class cluesRef extends App.ControllerContent
 
   clues: [
     {
-      container: '.search'
+      container: '.user-menu'
+      headline: 'Persönliches Menü'
+      text: 'Hier findest du den Logout, den Weg zu deinen Einstellungen und deinen Verlauf.'
+      actions: [
+        'click .user .js-action',
+        'hover .user'
+      ]
+    }
+    {
+      container: '.search-holder'
       headline: 'Suche'
-      text: 'Hier finden sie alles!'
+      text: 'Um alles zu finden nutze den <kbd>*</kbd>-Platzhalter'
     },
     {
       container: '.user-menu'
       headline: 'Erstellen'
-      text: 'Hier können sie Tickets, Kunden und Organisationen anlegen.'
-      action: 'click .add .js-action'
+      text: 'Hier kannst du Tickets, Kunden und Organisationen anlegen.'
+      actions: [
+        'click .add .js-action',
+        'hover .add'
+      ]
     }
   ]
 
   events:
     'click .js-next': 'next'
     'click .js-previous': 'previous'
+    'click .js-close': 'close'
 
   constructor: ->
     super
@@ -965,7 +978,7 @@ class cluesRef extends App.ControllerContent
 
     ###
 
-    @options.onComplete = ->
+    @options.onComplete = -> null
     @position = 0
     @render()
 
@@ -977,6 +990,10 @@ class cluesRef extends App.ControllerContent
     event.stopPropagation()
     @navigate -1
 
+  close: =>
+    @cleanUp()
+    @options.onComplete()
+
   navigate: (direction) ->
     @cleanUp()
     @position += direction
@@ -984,7 +1001,7 @@ class cluesRef extends App.ControllerContent
     if @position < @clues.length
       @render()
     else
-      @onComplete()
+      @options.onComplete()
 
   cleanUp: ->
     clue = @clues[@position]
@@ -992,66 +1009,147 @@ class cluesRef extends App.ControllerContent
     container.removeClass('selected-clue')
 
     # undo click perform by doing it again
-    if clue.action
-      @perform clue.action, container
+    if clue.actions
+      @perform clue.actions, container
 
-    @el.find('.clue').remove()
+    @$('.modal').remove()
 
   render: ->
     clue = @clues[@position]
     container = $(clue.container)
     container.addClass('selected-clue')
 
+    if clue.actions
+      @perform clue.actions, container
+
+    # calculate bounding box after actions
+    # to take toggled child nodes into account
+    boundingBox = @getVisibleBoundingBox(container.get(0))
+
     center =
-      x: container.offset().left + container.width()/2
-      y: container.offset().top + container.height()/2
+      x: boundingBox.left + boundingBox.width/2
+      y: boundingBox.top + boundingBox.height/2
 
     @html App.view('layout_ref/clues')
       headline: clue.headline
       text: clue.text
-      width: container.outerWidth()
-      height: container.outerHeight()
+      width: boundingBox.width
+      height: boundingBox.height
       center: center
       position: @position
       max: @clues.length
 
-    if clue.action
-      @perform clue.action, container
+    @placeWindow(boundingBox)
+
+  placeWindow: (target) ->
+    modalElement = @$('.js-positionOrigin')
+    modal = modalElement.get(0).getBoundingClientRect()
+    position = ''
+    left = 0
+    top = 0
+    maxWidth = $(window).width()
+    maxHeight = $(window).height()
+
+    # try to place it parallel to the larger side
+    if target.height > target.width
+      # try to place it aside
+      # prefer right
+      if target.right + modal.width <= maxWidth
+        left = target.right
+        position = 'right'
+      else 
+        # place left
+        left = target.left - modal.width
+        position = 'left'
+
+      if position
+        top = target.top + target.height/2 - modal.height/2
+    else if target.height <= target.width or !position
+      # try to place it above or below
+      # prefer above
+      if target.top - modal.height >= 0
+        top = target.top - modal.height
+        position = 'above'
+      else
+        top = target.bottom
+        position = 'below'
+
+      if position
+        left = target.left + target.width/2 - modal.width/2
+
+    # keep it inside the window
+    # horizontal
+    if left < 0
+      moveArrow = modal.width/2 + left
+      left = 0
+    else if left + modal.width > maxWidth
+      moveArrow = modal.width/2 + maxWidth - (left + modal.width)
+      left = maxWidth - modal.width
+
+    if top < 0
+      moveArrow = modal.height/2 + height
+      top = 0
+    else if top + modal.height > maxHeight
+      moveArrow = modal.height/2 + maxHeight - (top + modal.height)
+      top = maxHeight - modal.height
+
+    # show window
+    modalElement
+      .addClass "is-visible is-#{ position }"
+      .css
+        'left': left
+        'top': top
+
+    if moveArrow
+      parameter = if position is 'above' or position is 'below' then 'left' else 'top'
+      console.log("move arrow", position, parameter, moveArrow)
+      modalElement.find('.js-arrow').css(parameter, moveArrow)
+
+  getVisibleBoundingBox: (el) ->
+    ###
+
+      getBoundingClientRect doesn't take 
+      absolute-positioned child nodes into account
 
-    @placeWindow(container)
+    ###
+    children = el.querySelectorAll('*')
+    bb = el.getBoundingClientRect()
+    dimensions =
+      left: bb.left,
+      right: bb.right,
+      top: bb.top,
+      bottom: bb.bottom
 
-  placeWindow: (container) ->
-    clueWindow = @el.find('.clue-window')
+    for child in children
 
-    # see if we can place it above or below target
-    if clueWindow.outerHeight() + container.outerHeight() < $(window).outerHeight()
-      clueWindow.css('left', container.offset().left)
+      continue if getComputedStyle(child).position is not 'absolute'
 
-      # below or above ?
-      if container.offset().top + container.outerHeight() + clueWindow.outerHeight() < $(window).outerHeight()
-        # place below
-        clueWindow.css('top', container.offset().top + container.outerHeight())
-      else
-        # place above
-        clueWindow.css('top', container.offset().top - container.outerHeight() - clueWindow.outerHeight())
-    else
-      clueWindow.css('top', container.offset().top)
-      # okay, let's try right or left then
-      if container.offset().left + container.outerWidth() + clueWindow.outerWidth() < $(window).outerWidth()
-        # place right
-        clueWindow.css('left', container.offset().left + container.outerWidth())
-      else
-        # place left
-        clueWindow.css('left', container.offset().left - container.outerWidth() - clueWindow.outerWidth())
+      bb = child.getBoundingClientRect()
 
-    # show window
-    clueWindow.addClass('is-visible')
+      continue if bb.width is 0 or bb.height is 0
+
+      if bb.left < dimensions.left
+        dimensions.left = bb.left
+      if bb.top < dimensions.top
+        dimensions.top = bb.top
+      if bb.right > dimensions.right
+        dimensions.right = bb.right
+      if bb.bottom > dimensions.bottom
+        dimensions.bottom = bb.bottom
+
+    dimensions.width = dimensions.right - dimensions.left
+    dimensions.height = dimensions.bottom - dimensions.top
+
+    dimensions
 
+  perform: (actions, container) ->
+    for action in actions
+      eventName = action.substr 0, action.indexOf(' ')
+      selector = action.substr action.indexOf(' ') + 1
 
-  perform: (action, container) ->
-    eventName = action.substr 0, action.indexOf(' ')
-    selector = action.substr action.indexOf(' ') + 1
-    container.find(selector)[0][eventName]()
+      switch eventName
+        when 'click' then container.find(selector).trigger('click')
+        when 'hover' then container.find(selector).toggleClass('is-hovered')
 
 App.Config.set( 'layout_ref/clues', cluesRef, 'Routes' )
 

+ 15 - 11
app/assets/javascripts/app/views/layout_ref/clues.jst.eco

@@ -1,22 +1,26 @@
-<div class="clue">
-  <div class="clue-backdrop" style="background: 
+<div class="modal modal--clue">
+  <div class="modal-backdrop" style="background: 
     radial-gradient(
       ellipse <%= @width + 400 %>px <%= @height + 400 %>px 
       at <%= @center.x %>px <%= @center.y %>px,
       hsla(202,68%,54%,.4),
-      hsla(202,68%,54%,.8)
+      hsla(202,68%,54%,.9)
     )
   "></div>
-  <div class="clue-window">
-    <div class="clue-inner">
-      <div class="clue-header"><%= @headline %></div>
-      <div class="clue-body"><%= @text %></div>
-      <div class="clue-controls">
-        <div class="clue-control">
+  <div class="modal-spacer js-positionOrigin">
+    <div class="modal-content">
+      <div class="modal-arrow js-arrow"></div>
+      <div class="modal-close js-close">
+        <svg class="icon-diagonal-cross"><use xlink:href="#icon-diagonal-cross" /></svg>
+      </div>
+      <div class="modal-header"><%= @headline %></div>
+      <div class="modal-body"><%- @text %></div>
+      <div class="modal-controls">
+        <div class="modal-control">
           <div class="<% if @position is 0: %>is-disabled <% end %>btn btn--text js-previous"><%- @T( 'Previous' ) %></div>
         </div>
-        <div class="clue-control clue-count"><%= @position+1 %>/<%= @max %></div>
-        <div class="clue-control">
+        <div class="modal-control clue-count"><%= @position+1 %>/<%= @max %></div>
+        <div class="modal-control">
           <% if @position+1 is @max: %>
             <div class="btn btn--text js-next"><%- @T( 'Finish' ) %></div>
           <% else: %>

+ 0 - 9
app/assets/stylesheets/bootstrap.css

@@ -718,15 +718,6 @@ code {
   background-color: #f9f2f4;
   border-radius: 4px;
 }
-kbd {
-  padding: 2px 4px;
-  font-size: 90%;
-  color: #fff;
-  background-color: #333;
-  border-radius: 3px;
-  -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25);
-          box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25);
-}
 kbd kbd {
   padding: 0;
   font-size: 100%;

+ 97 - 64
app/assets/stylesheets/zammad.css.scss

@@ -19,6 +19,7 @@ body {
   background: hsl(210,17%,98%);
   height: 100%;
   color: hsl(198,19%,72%);
+  word-break: break-all;
 }
 
 p {
@@ -910,69 +911,100 @@ textarea,
   @extend .zIndex-9;
 }
 
-.clue {
-  position: fixed;
-  left: 0;
-  top: 0;
-  width: 100%;
-  height: 100%;
+.modal--clue {
+  display: flex;
+  align-items: center;
+  justify-content: center;
   @extend .zIndex-8;
-}
+  
+  .modal-backdrop {
+    bottom: 0;
+  }
 
-.clue-backdrop {
-  width: 100%;
-  height: 100%;
-  position: absolute;
-  left: 0;
-  top: 0;
-}
+  .modal-spacer {
+    position: absolute;
+    opacity: 0;
+    padding: 18px;
+    
+    &.is-visible {
+      opacity: 1;
+    }
+  }
 
-.clue-window {
-  position: absolute;
-  opacity: 1;
-  padding: 18px;
-  
-  &.is-visible {
-    opacity: 1;
+  .modal-arrow {
+    background: inherit;
+    width: 20px;
+    height: 20px;
+    position: absolute;
+    margin: -10px 0 53px -10px;
+    left: 0;
+    top: 50%;
+    transform: rotate(45deg);
   }
-}
 
-.clue-inner {
-  min-width: 260px;
-  padding: 24px 24px 14px;
-  background: white;
-  color: hsl(60,1%,34%);
-  box-shadow:
-    0 8px 17px 0 rgba(0, 0, 0, 0.1),
-    0 6px 20px 0 rgba(0, 0, 0, 0.05);
-}
+  .modal-spacer.is-above .modal-arrow {
+    left: 50%;
+    top: 100%;
+    background: hsl(210,5%,97%);
+  }
 
-.clue-controls {
-  background: hsl(210,5%,97%);
-  margin: 24px -24px -14px;
-  padding: 7px 10px;
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
-}
+  .modal-spacer.is-below .modal-arrow {
+    left: 50%;
+    top: 0;
+  }
 
-.clue-control {
-  padding-left: 14px;
-  padding-right: 14px;
-  
-  .btn.is-disabled {
-    opacity: 1;
-    color: hsl(240,5%,83%);
+  .modal-spacer.is-left .modal-arrow {
+    left: 100%;
+    top: 50%;
+  }
+
+  .modal-content {
+    border: none;
+    width: 300px;
+    box-shadow:
+      0 8px 17px 0 rgba(0, 0, 0, 0.1),
+      0 6px 20px 0 rgba(0, 0, 0, 0.05);
+  }
+
+  .modal-controls {
+    background: hsl(210,5%,97%);
+    margin: 23px 0 0;
+    padding: 7px 10px;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+  }
+
+  .modal-control {
+    padding-left: 14px;
+    padding-right: 14px;
+    
+    .btn.is-disabled {
+      opacity: 1;
+      color: hsl(240,5%,83%);
+    }
+  }
+
+  .modal-header {
+    padding-bottom: 7px;
+    font-size: 18px;
   }
-}
 
-.clue-header {
-  margin-bottom: 7px;
-  font-size: 18px;
+  .modal-body {
+    max-width: 340px;
+  }
 }
 
-.clue-body {
-  max-width: 340px;
+kbd {
+  background: hsl(200,8%,21%);
+  border-radius: 3px;
+  box-shadow: 0 1px 1px black;
+  color: white;
+  display: inline-block;
+  font-size: 12px;
+  margin: 0 1px;
+  padding: 0 4px;
+  vertical-align: top;
 }
 
 .form-stacked .checkbox label {
@@ -1828,7 +1860,7 @@ footer {
 }
 
 .search {
-  padding: 7px 5px 4px 10px;
+  padding: 11px 5px 4px 10px;
   border-bottom: 1px solid rgba(240, 250, 255, .05);
   flex-shrink: 0;
   display: flex;
@@ -1837,8 +1869,7 @@ footer {
   
   .search-holder {
     flex: 1;
-    margin-right: 2px;
-    margin-top: 4px;
+    border-radius: 15px;
     position: relative;
     transition: 240ms;
   }
@@ -1867,7 +1898,7 @@ footer {
 
   .search input {
     width: 100%;
-    padding: 5px 10px 5px 33px;
+    padding: 5px 33px 5px 33px;
     height: 30px;
     color: #ECECEC;
     background: #31373b;
@@ -1900,7 +1931,7 @@ footer {
   .search .logo {
     position: relative;
     @extend .u-clickable, .zIndex-5;
-    margin: 0 10px;
+    margin: -4px 10px 0 12px;
     transition: 240ms;
   }
 
@@ -1931,7 +1962,7 @@ footer {
     }
 
   .search .custom-dropdown-menu {
-    margin: -2px 0 0 0;
+    margin: 0;
     padding: 0;
     list-style: none;
     background: #26272e;
@@ -1939,7 +1970,7 @@ footer {
     left: 0;
     right: 0;
     bottom: 0;
-    top: 56px;
+    top: 53px;
     z-index: 1002;
     display: none;
     overflow: scroll;
@@ -1983,7 +2014,8 @@ footer {
       position: relative;
     }
 
-    .user-menu > li:hover .list-button:before {
+    .user-menu > li:hover .list-button:before,
+    .user-menu > li.is-hovered .list-button:before {
       content: '';
       position: absolute;
       top: 4px;
@@ -1993,7 +2025,8 @@ footer {
       background: white;
     }
 
-    .user-menu li.add:hover .list-button:before {
+    .user-menu li.add:hover .list-button:before,
+    .user-menu li.add.is-hovered .list-button:before {
       background: #38ae6a;
     }
 
@@ -2013,6 +2046,7 @@ footer {
     }
 
     .user-menu > li.add:hover .user-menu-icon.icon-plus,
+    .user-menu > li.add.is-hovered .user-menu-icon.icon-plus,
     .user-menu > li.add.active .user-menu-icon.icon-plus {
       fill: white;
     }
@@ -3425,7 +3459,6 @@ footer {
 
       .attachment-name {
         margin-right: 5px;
-        word-break: break-all;
         @extend .u-highlight;
       }
 
@@ -4052,7 +4085,7 @@ footer {
   border-radius: 0;
   border: 1px solid hsl(0,0%,90%);
   box-shadow: none;
-  color: hsl(206,7%,28%);
+  color: hsl(60,1%,34%);
   word-wrap: break-word;
 }
 
@@ -4079,6 +4112,7 @@ footer {
 
   .modal-body {
     padding: 0 23px;
+    word-break: normal;
   }
 
   .modal-footer {
@@ -4975,7 +5009,6 @@ label + .wizard-buttonList {
   }
 
   .name {
-    word-break: break-all;
     @extend .u-highlight;
   }
 }

BIN
contrib/icon-sprite.sketch