Browse Source

article meta fade-in-out

Felix Niklas 10 years ago
parent
commit
993e712821

+ 23 - 12
app/assets/javascripts/app/controllers/ticket_zoom.js.coffee

@@ -648,23 +648,34 @@ class ArticleView extends App.Controller
 
   more_toogle: (e) ->
     e.preventDefault()
-    articleMetaTop = $(e.target).closest('.ticket-article-item').find('.article-meta.top')
+    article = $(e.target).closest('.ticket-article-item')
+    metaTopClip = article.find('.article-meta-clip.top')
+    metaBottomClip = article.find('.article-meta-clip.bottom')
+    metaTop = article.find('.article-content-meta.top')
+    metaBottom = article.find('.article-content-meta.bottom')
+
+    if !metaTop.hasClass('hide')
+      article.removeClass('state--folde-out')
 
-    if !$(e.target).closest('.ticket-article-item').find('.article-meta.top').hasClass('hide')
       # scroll back up
-      articleMetaTop.scrollParent().scrollTop( articleMetaTop.scrollParent().scrollTop() - 2* articleMetaTop.outerHeight() )
+      TweenLite.to(article.scrollParent(), 0.5, { scrollTo: article.scrollParent().scrollTop() - metaTop.outerHeight() })
 
-      $(e.target).closest('.ticket-article-item').removeClass('state--folde-out')
-      $(e.target).closest('.ticket-article-item').find('.close-details').addClass('hide')
-      $(e.target).closest('.ticket-article-item').find('.article-content-meta.bottom').addClass('hide')
-      articleMetaTop.addClass('hide')
+      TweenLite.to(metaTop, 0.5, { y: 0, opacity: 0, onComplete: -> metaTop.addClass('hide') })
+      TweenLite.to(metaBottom, 0.5, { y: -metaBottom.outerHeight(), opacity: 0, onComplete: -> metaTop.addClass('hide') })
+      TweenLite.to(metaTopClip, 0.5, { height: 0 })
+      TweenLite.to(metaBottomClip, 0.5, { height: 0 })
     else
-      $(e.target).closest('.ticket-article-item').addClass('state--folde-out')
-      $(e.target).closest('.ticket-article-item').find('.close-details').removeClass('hide')
-      $(e.target).closest('.ticket-article-item').find('.article-content-meta.bottom').removeClass('hide')
-      articleMetaTop.removeClass('hide')
+      article.addClass('state--folde-out')
+      metaBottom.removeClass('hide')
+      metaTop.removeClass('hide')
+
       # balance out the top meta height by scrolling down
-      articleMetaTop.scrollParent().scrollTop( articleMetaTop.scrollParent().scrollTop() + articleMetaTop.outerHeight() )
+      TweenLite.to(article.scrollParent(), 0.5, { scrollTo: article.scrollParent().scrollTop() + metaTop.outerHeight() })
+
+      TweenLite.fromTo(metaTop, 0.5, { y: metaTop.outerHeight(), opacity: 0 }, { y: 0, opacity: 1 })
+      TweenLite.fromTo(metaBottom, 0.5, { y: -metaBottom.outerHeight(), opacity: 0 }, { y: 0, opacity: 1 })
+      TweenLite.to(metaTopClip, 0.5, { height: metaTop.outerHeight() })
+      TweenLite.to(metaBottomClip, 0.5, { height: metaBottom.outerHeight() })
 
   checkIfSignatureIsNeeded: (type) =>
 

File diff suppressed because it is too large
+ 11 - 0
app/assets/javascripts/app/lib/gsap/CSSPlugin.min.js


+ 120 - 0
app/assets/javascripts/app/lib/gsap/ScrollToPlugin.js

@@ -0,0 +1,120 @@
+/*!
+ * VERSION: 1.7.4
+ * DATE: 2014-07-17
+ * UPDATES AND DOCS AT: http://www.greensock.com
+ *
+ * @license Copyright (c) 2008-2014, GreenSock. All rights reserved.
+ * This work is subject to the terms at http://www.greensock.com/terms_of_use.html or for
+ * Club GreenSock members, the software agreement that was issued with your membership.
+ * 
+ * @author: Jack Doyle, jack@greensock.com
+ **/
+var _gsScope = (typeof(module) !== "undefined" && module.exports && typeof(global) !== "undefined") ? global : this || window; //helps ensure compatibility with AMD/RequireJS and CommonJS/Node
+(_gsScope._gsQueue || (_gsScope._gsQueue = [])).push( function() {
+
+	"use strict";
+
+	var _doc = document.documentElement,
+		_window = window,
+		_max = function(element, axis) {
+			var dim = (axis === "x") ? "Width" : "Height",
+				scroll = "scroll" + dim,
+				client = "client" + dim,
+				body = document.body;
+			return (element === _window || element === _doc || element === body) ? Math.max(_doc[scroll], body[scroll]) - (_window["inner" + dim] || Math.max(_doc[client], body[client])) : element[scroll] - element["offset" + dim];
+		},
+
+		ScrollToPlugin = _gsScope._gsDefine.plugin({
+			propName: "scrollTo",
+			API: 2,
+			version:"1.7.4",
+
+			//called when the tween renders for the first time. This is where initial values should be recorded and any setup routines should run.
+			init: function(target, value, tween) {
+				this._wdw = (target === _window);
+				this._target = target;
+				this._tween = tween;
+				if (typeof(value) !== "object") {
+					value = {y:value}; //if we don't receive an object as the parameter, assume the user intends "y".
+				}
+				this.vars = value;
+				this._autoKill = (value.autoKill !== false);
+				this.x = this.xPrev = this.getX();
+				this.y = this.yPrev = this.getY();
+				if (value.x != null) {
+					this._addTween(this, "x", this.x, (value.x === "max") ? _max(target, "x") : value.x, "scrollTo_x", true);
+					this._overwriteProps.push("scrollTo_x");
+				} else {
+					this.skipX = true;
+				}
+				if (value.y != null) {
+					this._addTween(this, "y", this.y, (value.y === "max") ? _max(target, "y") : value.y, "scrollTo_y", true);
+					this._overwriteProps.push("scrollTo_y");
+				} else {
+					this.skipY = true;
+				}
+				return true;
+			},
+
+			//called each time the values should be updated, and the ratio gets passed as the only parameter (typically it's a value between 0 and 1, but it can exceed those when using an ease like Elastic.easeOut or Back.easeOut, etc.)
+			set: function(v) {
+				this._super.setRatio.call(this, v);
+
+				var x = (this._wdw || !this.skipX) ? this.getX() : this.xPrev,
+					y = (this._wdw || !this.skipY) ? this.getY() : this.yPrev,
+					yDif = y - this.yPrev,
+					xDif = x - this.xPrev;
+
+				if (this._autoKill) {
+					//note: iOS has a bug that throws off the scroll by several pixels, so we need to check if it's within 7 pixels of the previous one that we set instead of just looking for an exact match.
+					if (!this.skipX && (xDif > 7 || xDif < -7) && x < _max(this._target, "x")) {
+						this.skipX = true; //if the user scrolls separately, we should stop tweening!
+					}
+					if (!this.skipY && (yDif > 7 || yDif < -7) && y < _max(this._target, "y")) {
+						this.skipY = true; //if the user scrolls separately, we should stop tweening!
+					}
+					if (this.skipX && this.skipY) {
+						this._tween.kill();
+						if (this.vars.onAutoKill) {
+							this.vars.onAutoKill.apply(this.vars.onAutoKillScope || this._tween, this.vars.onAutoKillParams || []);
+						}
+					}
+				}
+				if (this._wdw) {
+					_window.scrollTo((!this.skipX) ? this.x : x, (!this.skipY) ? this.y : y);
+				} else {
+					if (!this.skipY) {
+						this._target.scrollTop = this.y;
+					}
+					if (!this.skipX) {
+						this._target.scrollLeft = this.x;
+					}
+				}
+				this.xPrev = this.x;
+				this.yPrev = this.y;
+			}
+
+		}),
+		p = ScrollToPlugin.prototype;
+
+	ScrollToPlugin.max = _max;
+
+	p.getX = function() {
+		return (!this._wdw) ? this._target.scrollLeft : (_window.pageXOffset != null) ? _window.pageXOffset : (_doc.scrollLeft != null) ? _doc.scrollLeft : document.body.scrollLeft;
+	};
+
+	p.getY = function() {
+		return (!this._wdw) ? this._target.scrollTop : (_window.pageYOffset != null) ? _window.pageYOffset : (_doc.scrollTop != null) ? _doc.scrollTop : document.body.scrollTop;
+	};
+
+	p._kill = function(lookup) {
+		if (lookup.scrollTo_x) {
+			this.skipX = true;
+		}
+		if (lookup.scrollTo_y) {
+			this.skipY = true;
+		}
+		return this._super._kill.call(this, lookup);
+	};
+
+}); if (_gsScope._gsDefine) { _gsScope._gsQueue.pop()(); }

File diff suppressed because it is too large
+ 11 - 0
app/assets/javascripts/app/lib/gsap/TimelineLite.min.js


File diff suppressed because it is too large
+ 11 - 0
app/assets/javascripts/app/lib/gsap/TweenLite.min.js


+ 53 - 50
app/assets/javascripts/app/views/ticket_zoom/article_view.jst.eco

@@ -2,32 +2,34 @@
 <div class="ticket-article-item bubble-grid <%= article.sender.name.toLowerCase() %> <%= article.type.name %>" data-id="<%= article.id %>" id="article-<%= article.id %>">
 
 
-  <div class="article-content-meta">
-    <div class="article-meta top hide">
-    <% if article.from: %>
-      <div class="horizontal article-meta-row" title="<%- @Ti( 'From' ) %>: <%= article.from %>">
-        <div class="article-meta-key contain-text"><%- @T( 'From' ) %></div>
-        <div class="article-meta-value flex contain-text"><%= article.from %></div>
-      </div>
-    <% end %>
-    <% if article.to: %>
-      <div class="horizontal article-meta-row" title="<%- @Ti( 'To' ) %>: <%= article.to %>">
-        <div class="article-meta-key contain-text"><%- @T( 'To' ) %></div>
-        <div class="article-meta-value flex contain-text"><%= article.to %></div>
-      </div>
-    <% end %>
-    <% if article.cc: %>
-      <div class="horizontal article-meta-row" title="<%- @Ti( 'Kopie' ) %>: <%= article.cc %>">
-        <div class="article-meta-key contain-text"><%- @T( 'Kopie' ) %></div>
-        <div class="article-meta-value flex contain-text"><%= article.cc %></div>
-      </div>
-    <% end %>
-    <% if article.subject: %>
-      <div class="horizontal article-meta-row" title="<%- @Ti( 'Subject' ) %>: <%= article.subject %>">
-        <div class="article-meta-key contain-text"><%- @T( 'Subject' ) %></div>
-        <div class="article-meta-value flex contain-text"><%= article.subject %></div>
+  <div class="article-meta-clip top">
+    <div class="article-content-meta top hide">
+      <div class="article-meta top">
+      <% if article.from: %>
+        <div class="horizontal article-meta-row" title="<%- @Ti( 'From' ) %>: <%= article.from %>">
+          <div class="article-meta-key contain-text"><%- @T( 'From' ) %></div>
+          <div class="article-meta-value flex contain-text"><%= article.from %></div>
+        </div>
+      <% end %>
+      <% if article.to: %>
+        <div class="horizontal article-meta-row" title="<%- @Ti( 'To' ) %>: <%= article.to %>">
+          <div class="article-meta-key contain-text"><%- @T( 'To' ) %></div>
+          <div class="article-meta-value flex contain-text"><%= article.to %></div>
+        </div>
+      <% end %>
+      <% if article.cc: %>
+        <div class="horizontal article-meta-row" title="<%- @Ti( 'Kopie' ) %>: <%= article.cc %>">
+          <div class="article-meta-key contain-text"><%- @T( 'Kopie' ) %></div>
+          <div class="article-meta-value flex contain-text"><%= article.cc %></div>
+        </div>
+      <% end %>
+      <% if article.subject: %>
+        <div class="horizontal article-meta-row" title="<%- @Ti( 'Subject' ) %>: <%= article.subject %>">
+          <div class="article-meta-key contain-text"><%- @T( 'Subject' ) %></div>
+          <div class="article-meta-value flex contain-text"><%= article.subject %></div>
+        </div>
+      <% end %>
       </div>
-    <% end %>
     </div>
   </div>
 
@@ -36,37 +38,38 @@
     <div class="flex text-bubble <%= ' internal' if article.internal is true %>"><div class="bubble-arrow"></div><%- article.html %></div>
   </div>
 
-  <div class="article-content-meta bottom hide">
-    <div class="article-meta bottom">
-      <div class="horizontal article-meta-row">
-        <div class="article-meta-key"><%- @T( 'Kanal' ) %></div>
-        <div class="article-meta-value">
-          <span class="white <%- article.type.name %> channel icon"></span>
-          <%- @T(article.type.name) %>
-          <% if article.type.name is 'email': %>
-          <a class="text-muted" href="<%= App.Config.get('api_path') %>/ticket_article_plain/<%= article.id %>"><%- @T( 'raw' ) %></a>
-          <% end %>
+  <div class="article-meta-clip bottom">
+    <div class="article-content-meta bottom hide">
+      <div class="article-meta bottom">
+        <div class="horizontal article-meta-row">
+          <div class="article-meta-key"><%- @T( 'Kanal' ) %></div>
+          <div class="article-meta-value">
+            <span class="white <%- article.type.name %> channel icon"></span>
+            <%- @T(article.type.name) %>
+            <% if article.type.name is 'email': %>
+            <a class="text-muted" href="<%= App.Config.get('api_path') %>/ticket_article_plain/<%= article.id %>"><%- @T( 'raw' ) %></a>
+            <% end %>
+          </div>
         </div>
       </div>
-    </div>
-    <% if article.attachments: %>
-      <div class="always-shown">
-      <% for attachment in article.attachments: %>
-        <a href="<%= App.Config.get('api_path') %>/ticket_attachment/<%= article.ticket_id %>/<%= article.id %>/<%= attachment.id %>" target="_blank" data-type="attachment" class="attachment" title="<%= attachment.size %>"><%= attachment.filename %></a>
+      <% if article.attachments: %>
+        <div class="always-shown">
+        <% for attachment in article.attachments: %>
+          <a href="<%= App.Config.get('api_path') %>/ticket_attachment/<%= article.ticket_id %>/<%= article.id %>/<%= attachment.id %>" target="_blank" data-type="attachment" class="attachment" title="<%= attachment.size %>"><%= attachment.filename %></a>
+        <% end %>
+        </div>
       <% end %>
-      </div>
-    <% end %>
 
-    <% if article.actions: %>
-    <div class="article-actions horizontal stretch">
-      <% for action in article.actions: %>
-        <a href="<%= action.href %>" data-type="<%= action.type %>" class="article-action<% if action.class: %> <%= action.class %><% end %>">
-          <span class="<%= action.type %> icon"></span><%- @T( action.name ) %>
-        </a>
+      <% if article.actions: %>
+      <div class="article-actions horizontal stretch">
+        <% for action in article.actions: %>
+          <a href="<%= action.href %>" data-type="<%= action.type %>" class="article-action<% if action.class: %> <%= action.class %><% end %>">
+            <span class="<%= action.type %> icon"></span><%- @T( action.name ) %>
+          </a>
+        <% end %>
+      </div>
       <% end %>
     </div>
-    <% end %>
-    <div class="close-details hide"><%- @T('close details') %></div>
   </div>
 
   <small class="task-subline"><time class="humanTimeFromNow" datetime="<%- article.created_at %>" data-time="<%- article.created_at %>">?</time></small>

+ 2 - 0
app/assets/javascripts/application.js

@@ -30,6 +30,8 @@
 
 //= require_tree ./app/lib/base
 
+//= require_tree ./app/lib/gsap
+
 //= require ./app/index.js.coffee
 
 // IE8 workaround for missing console.log

+ 46 - 29
app/assets/stylesheets/zzz.css.erb

@@ -2403,27 +2403,35 @@ footer {
   max-width: 800px;
   margin: 0 auto;
   padding: 0 21px;
+  overflow: hidden;
 }
 
   .bubble-grid .avatar {
     margin-right: 15px;
+    background: rgba(0,0,0,.05);
   }
 
+.ticket-article-item {
+  padding-bottom: 33px;
+  position: relative;
+}
+
   .customer.ticket-article-item .avatar {
     margin-right: 0;
     margin-left: 15px;
   }
 
-  .ticket-article-item {
-    margin-top: 33px;
-    margin-bottom: 33px;
-  }
+  /* 
+    clip the article-meta to not stand out on the other side 
+    of the text-bubble if the text bubble is small
+  */
 
-  .article-content-meta {
-    margin-left: 55px;
-    margin-right: 55px;
+  .article-meta-clip {
+    overflow: hidden;
     position: relative;
+    height: 100%;
   }
+
   .article-content {
     margin-right: 55px;
   }
@@ -2433,22 +2441,14 @@ footer {
       margin-left: 55px;
     }
 
-  .article-content-meta .more,
-  .article-content-meta .close-details,
-  .article-action {
-    margin: 5px 12px;
-    color: hsl(200,87%,45%);
-    font-size: 12px;
-    text-decoration: underline;
-    cursor: pointer;
-    -webkit-user-select: none;
-            user-select: none;
-  }
-
-  .article-content-meta .close-details {
+  .article-content-meta {
+    padding: 0 55px;
     position: absolute;
-    right: 5px;
-    bottom: -33px;
+    width: 100%;
+  }
+  .article-content {
+    position: relative;
+    z-index: 1;
   }
 
   .article-meta {
@@ -2489,18 +2489,26 @@ footer {
     padding: 10px 20px;
     white-space: pre-wrap;
     background: white;
-    border-radius: 5px;
+    border-radius: 2px;
     border: 1px solid hsl(240,4%,95%);
     box-shadow: 0 0 1px rgba(0,0,0,.06) inset;
     position: relative;
   }
 
+    .ticket-article-item.state--folde-out .text-bubble {
+      border-color: hsl(0,0%,90%);
+    }
+
   .customer.ticket-article-item .text-bubble {
     background: #e5f0f5;
     border-color: hsl(199,38%,92%);
     box-shadow: none;
   }
 
+    .customer.ticket-article-item.state--folde-out .text-bubble {
+      border-color: hsl(199,44%,85%);
+    }
+
   .ticket-article-item .text-bubble.internal {
     background: #f2def2;
     border-color: #eed3d7;
@@ -2511,6 +2519,7 @@ footer {
     padding: 0;
     border-color: #b3b3b3;
     white-space: normal;
+    border-radius: 5px;
   }
 
     .new-article textarea {
@@ -2591,18 +2600,26 @@ footer {
   }
 
   .article-action {
-    text-align: center;
     padding: 5px;
+    margin: 5px 12px;
+    color: black;
+    font-size: 12px;
+    text-align: center;
+    opacity: 0.5;
+    cursor: pointer;
+    -webkit-user-select: none;
+            user-select: none;
   }
 
+    .article-action:hover {
+      color: black;
+      text-decoration: none;
+      opacity: 1;
+    }
+
     .article-action .icon {
       margin-right: 10px;
       vertical-align: bottom;
-      opacity: 0.33;
-    }
-
-    .article-action:hover .icon {
-      opacity: 0.75;
     }
 
 .ticket-edit {

Some files were not shown because too many files changed in this diff