Admin Chat Preview: fix for widescreen & add animation

Turns out that the chat preview was broken on big screens. The website screenshot didn’t span the whole width and the chat was getting cut off.

Fixes:
- preserve 16:10 browser aspect ratio
- rename zoom to 1:1
- hide 1:1 mode on big screens that don’t need it
- scale the website screenshot to the full width
- scale the website screenshot in 1:1 mode with the chat
- make color picker work with changes
- use dynamic height in the form preview
This commit is contained in:
Felix Niklas 2019-01-07 17:06:10 +01:00 committed by Thorsten Eckel
parent 577bb5f723
commit 5239c83e71
4 changed files with 143 additions and 99 deletions

View file

@ -5,7 +5,7 @@ class App.ChannelChat extends App.ControllerSubContent
'change .js-params': 'updateParams' 'change .js-params': 'updateParams'
'input .js-params': 'updateParams' 'input .js-params': 'updateParams'
'submit .js-demo-head': 'onUrlSubmit' 'submit .js-demo-head': 'onUrlSubmit'
'click .js-selectBrowserWidth': 'selectBrowserWidth' 'click .js-selectBrowserSize': 'selectBrowserSize'
'click .js-swatch': 'usePaletteColor' 'click .js-swatch': 'usePaletteColor'
'click .js-toggle-chat': 'toggleChat' 'click .js-toggle-chat': 'toggleChat'
'change .js-chatSetting input': 'toggleChatSetting' 'change .js-chatSetting input': 'toggleChatSetting'
@ -104,8 +104,7 @@ class App.ChannelChat extends App.ControllerSubContent
] ]
isOpen: true isOpen: true
browserWidth: 1280 browserSize: 'desktop'
browserWidthMax: 1280
previewUrl: '' previewUrl: ''
previewScale: 1 previewScale: 1
@ -167,45 +166,33 @@ class App.ChannelChat extends App.ControllerSubContent
$(window).off 'resize.chat-designer' $(window).off 'resize.chat-designer'
@website.off('click.eyedropper') @website.off('click.eyedropper')
selectBrowserWidth: (event) => selectBrowserSize: (event) =>
tab = $(event.target).closest('[data-value]') tab = $(event.target).closest('[data-size]')
# select tab # select tab
tab.addClass('is-selected').siblings().removeClass('is-selected') tab.addClass('is-selected').siblings().removeClass('is-selected')
@browserWidth = tab.attr('data-value') @browserSize = tab.attr('data-size')
@updatePreview() @updatePreview()
updatePreview: (animate = true) => updatePreview: (animate = true) =>
width = parseInt @browserWidth, 10
# reset zoom # reset zoom
@chat @chat
.removeClass('is-fullscreen') .removeClass('is-fullscreen')
.toggleClass('no-transition', !animate) .toggleClass('no-transition', !animate)
.css 'transform', "translateY(#{ @getChatOffset() }px)" .css 'transform', "translateY(#{ @getChatOffset() }px)"
@browser.css('width', '') @browser.attr('data-size', @browserSize)
@website.css
transform: ''
width: ''
height: ''
@previewScale = 1 @previewScale = 1
return if @browserWidth is 'fit' switch @browserSize
when 'mobile'
if width < @el.width() && width == 375 @chat.addClass('is-fullscreen').css 'transform', "translateY(#{ @getChatOffset(true) }px)"
@chat.addClass('is-fullscreen').css 'transform', "translateY(#{ @getChatOffset(true) }px)" when '1:1'
@browser.css('width', "#{ width }px") @previewScale = Math.max(1, 1280/@el.width())
else @website.css 'transform', "scale(#{ @previewScale })"
if @el.width() > width && width == @browserWidthMax when 'desktop'
@previewScale = 1 scale = Math.min(1, @el.width()/1280) # don't use it for the previewScale (used for the color picker)
else @website.css 'transform', ''
@previewScale = @el.width()/width @chat.css 'transform', "translateY(#{ @getChatOffset() * scale }px) scale(#{ scale })"
@chat.css 'transform', "translateY(#{ @getChatOffset() * @previewScale }px) scale(#{ @previewScale })"
@website.css
transform: "scale(#{ @previewScale })"
width: @el.width() / @previewScale
height: @browserBody.height() / @previewScale
getChatOffset: (fullscreen) -> getChatOffset: (fullscreen) ->
return 0 if @isOpen return 0 if @isOpen
@ -296,8 +283,11 @@ class App.ChannelChat extends App.ControllerSubContent
@eyedropper.addClass('is-active') @eyedropper.addClass('is-active')
onColorPicked: (event) => onColorPicked: (event) =>
x = event.pageX - @website.offset().left website_x = @website.position().left
y = event.pageY - @website.offset().top website_y = @website.position().top
relative_x = event.pageX - @browserBody.offset().left
relative_y = event.pageY - @browserBody.offset().top
image = new Image() image = new Image()
image.src = @_screenshotSource image.src = @_screenshotSource
@ -305,11 +295,11 @@ class App.ChannelChat extends App.ControllerSubContent
canvas = document.createElement('canvas') canvas = document.createElement('canvas')
ctx = canvas.getContext('2d') ctx = canvas.getContext('2d')
canvas.width = image.width canvas.width = @browserBody.width()
canvas.height = image.height canvas.height = @browserBody.height()
ctx.drawImage(image, 0, 0, @previewScale * canvas.width, @previewScale * canvas.height) ctx.drawImage(image, website_x, website_y, @website.width() * @previewScale, @website.width() * @previewScale)
pixels = ctx.getImageData(x, y, 1, 1).data pixels = ctx.getImageData(relative_x, relative_y, 1, 1).data
@colorField.val("rgb(#{pixels.slice(0,3).join(',')})").trigger('change') @colorField.val("rgb(#{pixels.slice(0,3).join(',')})").trigger('change')

View file

@ -19,62 +19,64 @@
<div class="form-group"> <div class="form-group">
<label class="formGroup-label"><%- @T('Preview Width') %></label> <label class="formGroup-label"><%- @T('Preview Width') %></label>
<div class="control"> <div class="control">
<div class="select-tabs js-selectBrowserWidth"> <div class="select-tabs js-selectBrowserSize">
<div class="tab" data-value="375">iPhone 6</div> <div class="tab" data-size="mobile">Mobile</div>
<div class="tab" data-value="fit">1:1</div> <div class="tab" data-size="1:1">1:1</div>
<div class="tab is-selected" data-value="1280">MacBook</div> <div class="tab is-selected" data-size="desktop">Desktop</div>
</div> </div>
</div> </div>
</div> </div>
<label for="preview-iframe" class="formGroup-label"><%- @T('Preview') %></label> <label for="preview-iframe" class="formGroup-label"><%- @T('Preview') %></label>
<div class="browser chat-demo js-browser"> <div class="browser chat-demo js-browser" data-size="desktop">
<form class="browser-head js-demo-head" novalidate> <div class="browser-ratio">
<div class="browser-input"> <form class="browser-head js-demo-head" novalidate>
<input type="url" class="js-testurl-input" id="preview-iframe" value="<%= @previewUrl %>" placeholder="www.zammad.org"> <div class="browser-input">
<div class="loading icon small muted"></div> <input type="url" class="js-testurl-input" id="preview-iframe" value="<%= @previewUrl %>" placeholder="www.zammad.org">
</div> <div class="loading icon small muted"></div>
<input type="submit" class="btn" value="<%- @Ti('Load') %>"> </div>
</form> <input type="submit" class="btn" value="<%- @Ti('Load') %>">
<div class="browser-body js-browserBody"> </form>
<div class="browser-website js-website"> <div class="browser-body js-browserBody">
<img class="js-screenshot"> <div class="browser-website js-website">
</div> <img class="browser-website-background js-screenshot">
<style>@import "/assets/chat/chat.css";</style> </div>
<div class="chat-demo-animationHolder"> <style>@import "/assets/chat/chat.css";</style>
<div class="js-chat zammad-chat zammad-chat-is-open zammad-chat-is-loaded zammad-chat-is-shown is-open"> <div class="chat-demo-animationHolder">
<div class="zammad-chat-header js-toggle-chat js-backgroundColor js-chatHeader"> <div class="js-chat zammad-chat zammad-chat-is-open zammad-chat-is-loaded zammad-chat-is-shown is-open">
<div class="zammad-chat-header-controls"> <div class="zammad-chat-header js-toggle-chat js-backgroundColor js-chatHeader">
<span class="zammad-chat-agent-status" data-status="online"><%- @T('Online') %></span> <div class="zammad-chat-header-controls">
<span class="zammad-chat-header-icon"> <span class="zammad-chat-agent-status" data-status="online"><%- @T('Online') %></span>
<svg class="zammad-chat-header-icon-open" viewBox="0 0 13 7"><path d="M10.807 7l1.4-1.428-5-4.9L6.5-.02l-.7.7-4.9 4.9 1.414 1.413L6.5 2.886 10.807 7z" fill-rule="evenodd"></path></svg> <span class="zammad-chat-header-icon">
<svg class="zammad-chat-header-icon-close" viewBox="0 0 13 13"><path d="m2.241.12l-2.121 2.121 4.243 4.243-4.243 4.243 2.121 2.121 4.243-4.243 4.243 4.243 2.121-2.121-4.243-4.243 4.243-4.243-2.121-2.121-4.243 4.243-4.243-4.243" fill-rule="evenodd"></path></svg> <svg class="zammad-chat-header-icon-open" viewBox="0 0 13 7"><path d="M10.807 7l1.4-1.428-5-4.9L6.5-.02l-.7.7-4.9 4.9 1.414 1.413L6.5 2.886 10.807 7z" fill-rule="evenodd"></path></svg>
</span> <svg class="zammad-chat-header-icon-close" viewBox="0 0 13 13"><path d="m2.241.12l-2.121 2.121 4.243 4.243-4.243 4.243 2.121 2.121 4.243-4.243 4.243 4.243 2.121-2.121-4.243-4.243 4.243-4.243-2.121-2.121-4.243 4.243-4.243-4.243" fill-rule="evenodd"></path></svg>
</span>
</div>
<div class="zammad-chat-agent">
<img class="zammad-chat-agent-avatar" src="/assets/images/chat-demo-avatar.png">
<span class="zammad-chat-agent-sentence">
<span class="zammad-chat-agent-name"><%- @T('John Smith') %></span>
</span>
</div>
<div class="zammad-chat-welcome zammad-chat-is-hidden">
<svg class="zammad-chat-icon" viewBox="0 0 24 24"><path d="M2 5C2 4 3 3 4 3h16c1 0 2 1 2 2v10C22 16 21 17 20 17H4C3 17 2 16 2 15V5zM12 17l6 4v-4h-6z" fill-rule="evenodd"></path></svg>
<span class="zammad-chat-welcome-text js-chat-welcome"><strong>Chat</strong> with us!</span>
</div>
</div> </div>
<div class="zammad-chat-agent"> <div class="zammad-chat-body">
<img class="zammad-chat-agent-avatar" src="/assets/images/chat-demo-avatar.png"> <div class="zammad-chat-timestamp"><strong><%- @T('today') %></strong> 14:45</div>
<span class="zammad-chat-agent-sentence"> <div class="zammad-chat-message zammad-chat-message--customer">
<span class="zammad-chat-agent-name"><%- @T('John Smith') %></span> <span class="zammad-chat-message-body js-backgroundColor"><%- @T('Hello! I need help with your product.') %></span>
</span> </div>
</div> <div class="zammad-chat-message zammad-chat-message--agent">
<div class="zammad-chat-welcome zammad-chat-is-hidden"> <span class="zammad-chat-message-body"><%- @T('Hi! Which one of our products?') %></span>
<svg class="zammad-chat-icon" viewBox="0 0 24 24"><path d="M2 5C2 4 3 3 4 3h16c1 0 2 1 2 2v10C22 16 21 17 20 17H4C3 17 2 16 2 15V5zM12 17l6 4v-4h-6z" fill-rule="evenodd"></path></svg> </div>
<span class="zammad-chat-welcome-text js-chat-welcome"><strong>Chat</strong> with us!</span>
</div> </div>
<form class="zammad-chat-controls">
<textarea class="zammad-chat-input" rows="1" placeholder="Ihre Nachricht..."></textarea>
<button type="submit" class="zammad-chat-button zammad-chat-send js-backgroundColor"><%- @T('Send') %></button>
</form>
</div> </div>
<div class="zammad-chat-body">
<div class="zammad-chat-timestamp"><strong><%- @T('today') %></strong> 14:45</div>
<div class="zammad-chat-message zammad-chat-message--customer">
<span class="zammad-chat-message-body js-backgroundColor"><%- @T('Hello! I need help with your product.') %></span>
</div>
<div class="zammad-chat-message zammad-chat-message--agent">
<span class="zammad-chat-message-body"><%- @T('Hi! Which one of our products?') %></span>
</div>
</div>
<form class="zammad-chat-controls">
<textarea class="zammad-chat-input" rows="1" placeholder="Ihre Nachricht..."></textarea>
<button type="submit" class="zammad-chat-button zammad-chat-send js-backgroundColor"><%- @T('Send') %></button>
</form>
</div> </div>
</div> </div>
</div> </div>

View file

@ -117,12 +117,14 @@
<label class="formGroup-label"><%- @T('Preview') %></label> <label class="formGroup-label"><%- @T('Preview') %></label>
<div class="browser form-demo js-browser"> <div class="browser js-browser">
<div class="browser-body js-browserBody"> <div class="browser-ratio">
<div class="browser-website centered vertical fit"> <div class="browser-body js-browserBody">
<div class="btn js-formBtn"><%- @T('Feedback') %></div> <div class="browser-website centered vertical fit">
<div class="js-formInline" style="width: 300px;"></div> <div class="btn js-formBtn"><%- @T('Feedback') %></div>
<script id="zammad_form_script" src="/assets/form/form.js"></script> <div class="js-formInline" style="width: 300px;"></div>
<script id="zammad_form_script" src="/assets/form/form.js"></script>
</div>
</div> </div>
</div> </div>
</div> </div>

View file

@ -10040,32 +10040,82 @@ output {
.browser { .browser {
margin: 0 0 20px; margin: 0 0 20px;
border: 1px solid hsl(0,0%,90%);
border-radius: 5px;
position: relative; position: relative;
transition: 500ms;
width: 100%; &[data-size] {
padding-bottom: 65%; // 16:10 aspect ratio;
.browser-ratio {
width: 100%;
height: 100%;
position: absolute;
left: 0;
top: 0;
transition: 500ms;
display: flex;
flex-direction: column;
}
.browser-body {
padding: 0;
}
}
&[data-size="mobile"] .browser-ratio {
width: 375px;
}
&-ratio {
border: 1px solid hsl(0,0%,90%);
border-radius: 5px;
}
}
@media screen and (min-width: #{1280 + $sidebarWidth + $navigationWidth}) {
// hide 1:1 button when it gets inrelevant
[data-size="1:1"] {
display: none;
}
.browser[data-size="mobile"] .browser-ratio {
width: 500px;
}
} }
.browser-body { .browser-body {
flex: 1;
position: relative; position: relative;
overflow: hidden; overflow: hidden;
height: 450px; padding: 20px;
width: 100%;
&-inner {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
}
.browser-website { .browser-website {
position: relative; position: relative;
height: 100%; height: 100%;
transform-origin: left top; transform-origin: right bottom;
transition: 500ms;
overflow: hidden; overflow: hidden;
&.is-picking { &.is-picking {
cursor: image_url("/assets/images/eyedropper.gif") 0 15, auto; cursor: image_url("/assets/images/eyedropper.gif") 0 15, auto;
} }
}
img { &-background {
vertical-align: bottom; position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
object-position: left top;
}
} }
} }