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)"
@browser.css('width', "#{ width }px") when '1:1'
else @previewScale = Math.max(1, 1280/@el.width())
if @el.width() > width && width == @browserWidthMax @website.css 'transform', "scale(#{ @previewScale })"
@previewScale = 1 when 'desktop'
else scale = Math.min(1, @el.width()/1280) # don't use it for the previewScale (used for the color picker)
@previewScale = @el.width()/width @website.css 'transform', ''
@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,16 +19,17 @@
<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">
<div class="browser-ratio">
<form class="browser-head js-demo-head" novalidate> <form class="browser-head js-demo-head" novalidate>
<div class="browser-input"> <div class="browser-input">
<input type="url" class="js-testurl-input" id="preview-iframe" value="<%= @previewUrl %>" placeholder="www.zammad.org"> <input type="url" class="js-testurl-input" id="preview-iframe" value="<%= @previewUrl %>" placeholder="www.zammad.org">
@ -38,7 +39,7 @@
</form> </form>
<div class="browser-body js-browserBody"> <div class="browser-body js-browserBody">
<div class="browser-website js-website"> <div class="browser-website js-website">
<img class="js-screenshot"> <img class="browser-website-background js-screenshot">
</div> </div>
<style>@import "/assets/chat/chat.css";</style> <style>@import "/assets/chat/chat.css";</style>
<div class="chat-demo-animationHolder"> <div class="chat-demo-animationHolder">
@ -79,6 +80,7 @@
</div> </div>
</div> </div>
</div> </div>
</div>
<form class="js-params"> <form class="js-params">
<fieldset> <fieldset>

View file

@ -117,7 +117,8 @@
<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-ratio">
<div class="browser-body js-browserBody"> <div class="browser-body js-browserBody">
<div class="browser-website centered vertical fit"> <div class="browser-website centered vertical fit">
<div class="btn js-formBtn"><%- @T('Feedback') %></div> <div class="btn js-formBtn"><%- @T('Feedback') %></div>
@ -126,6 +127,7 @@
</div> </div>
</div> </div>
</div> </div>
</div>
<h3><%- @T('Requirements') %></h3> <h3><%- @T('Requirements') %></h3>
<p><%- @T("Zammad Forms requires jQuery. If you don't already use it on your website include it like this:") %></p> <p><%- @T("Zammad Forms requires jQuery. If you don't already use it on your website include it like this:") %></p>
<pre><code class="language-html js-code">&lt;script src="https://code.jquery.com/jquery-2.1.4.min.js"&gt;&lt;/script&gt;</code></pre> <pre><code class="language-html js-code">&lt;script src="https://code.jquery.com/jquery-2.1.4.min.js"&gt;&lt;/script&gt;</code></pre>

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;
&[data-size] {
padding-bottom: 65%; // 16:10 aspect ratio;
.browser-ratio {
width: 100%; width: 100%;
height: 100%;
position: absolute;
left: 0;
top: 0;
transition: 500ms;
display: flex;
flex-direction: column;
} }
.browser-body { .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 {
flex: 1;
position: relative; position: relative;
overflow: hidden; overflow: hidden;
height: 450px; padding: 20px;
&-inner {
position: absolute;
left: 0;
top: 0;
width: 100%; 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;
}
} }
} }