article meta fade-in-out

This commit is contained in:
Felix Niklas 2014-08-27 12:45:21 +02:00
parent 8d88baa040
commit 993e712821
8 changed files with 282 additions and 92 deletions

View file

@ -648,23 +648,34 @@ class ArticleView extends App.Controller
more_toogle: (e) -> more_toogle: (e) ->
e.preventDefault() 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 # 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') TweenLite.to(metaTop, 0.5, { y: 0, opacity: 0, onComplete: -> metaTop.addClass('hide') })
$(e.target).closest('.ticket-article-item').find('.close-details').addClass('hide') TweenLite.to(metaBottom, 0.5, { y: -metaBottom.outerHeight(), opacity: 0, onComplete: -> metaTop.addClass('hide') })
$(e.target).closest('.ticket-article-item').find('.article-content-meta.bottom').addClass('hide') TweenLite.to(metaTopClip, 0.5, { height: 0 })
articleMetaTop.addClass('hide') TweenLite.to(metaBottomClip, 0.5, { height: 0 })
else else
$(e.target).closest('.ticket-article-item').addClass('state--folde-out') article.addClass('state--folde-out')
$(e.target).closest('.ticket-article-item').find('.close-details').removeClass('hide') metaBottom.removeClass('hide')
$(e.target).closest('.ticket-article-item').find('.article-content-meta.bottom').removeClass('hide') metaTop.removeClass('hide')
articleMetaTop.removeClass('hide')
# balance out the top meta height by scrolling down # 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) => checkIfSignatureIsNeeded: (type) =>

File diff suppressed because one or more lines are too long

View file

@ -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 one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -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="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-clip top">
<div class="article-meta top hide"> <div class="article-content-meta top hide">
<% if article.from: %> <div class="article-meta top">
<div class="horizontal article-meta-row" title="<%- @Ti( 'From' ) %>: <%= article.from %>"> <% if article.from: %>
<div class="article-meta-key contain-text"><%- @T( 'From' ) %></div> <div class="horizontal article-meta-row" title="<%- @Ti( 'From' ) %>: <%= article.from %>">
<div class="article-meta-value flex contain-text"><%= article.from %></div> <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> </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> </div>
</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 class="flex text-bubble <%= ' internal' if article.internal is true %>"><div class="bubble-arrow"></div><%- article.html %></div>
</div> </div>
<div class="article-content-meta bottom hide"> <div class="article-meta-clip bottom">
<div class="article-meta bottom"> <div class="article-content-meta bottom hide">
<div class="horizontal article-meta-row"> <div class="article-meta bottom">
<div class="article-meta-key"><%- @T( 'Kanal' ) %></div> <div class="horizontal article-meta-row">
<div class="article-meta-value"> <div class="article-meta-key"><%- @T( 'Kanal' ) %></div>
<span class="white <%- article.type.name %> channel icon"></span> <div class="article-meta-value">
<%- @T(article.type.name) %> <span class="white <%- article.type.name %> channel icon"></span>
<% if article.type.name is 'email': %> <%- @T(article.type.name) %>
<a class="text-muted" href="<%= App.Config.get('api_path') %>/ticket_article_plain/<%= article.id %>"><%- @T( 'raw' ) %></a> <% if article.type.name is 'email': %>
<% end %> <a class="text-muted" href="<%= App.Config.get('api_path') %>/ticket_article_plain/<%= article.id %>"><%- @T( 'raw' ) %></a>
<% end %>
</div>
</div> </div>
</div> </div>
</div> <% if article.attachments: %>
<% if article.attachments: %> <div class="always-shown">
<div class="always-shown"> <% for attachment in article.attachments: %>
<% 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>
<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 %> <% end %>
</div>
<% end %>
<% if article.actions: %> <% if article.actions: %>
<div class="article-actions horizontal stretch"> <div class="article-actions horizontal stretch">
<% for action in article.actions: %> <% for action in article.actions: %>
<a href="<%= action.href %>" data-type="<%= action.type %>" class="article-action<% if action.class: %> <%= action.class %><% end %>"> <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 ) %> <span class="<%= action.type %> icon"></span><%- @T( action.name ) %>
</a> </a>
<% end %>
</div>
<% end %> <% end %>
</div> </div>
<% end %>
<div class="close-details hide"><%- @T('close details') %></div>
</div> </div>
<small class="task-subline"><time class="humanTimeFromNow" datetime="<%- article.created_at %>" data-time="<%- article.created_at %>">?</time></small> <small class="task-subline"><time class="humanTimeFromNow" datetime="<%- article.created_at %>" data-time="<%- article.created_at %>">?</time></small>

View file

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

View file

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