Refactor ZenMode

- No longer depends on the "hidden checkbox".
- No longer depends on manually storing/restoring the scroll position.
  Instead, we take advantage of jquery.scrollTo.
- Event-based.
- Simplifies the state-based styling.
This commit is contained in:
Robert Speicher 2016-01-08 23:33:08 -05:00
parent 6d95444108
commit 89ca4f04f6
5 changed files with 114 additions and 123 deletions

View file

@ -1,56 +1,80 @@
# Zen Mode (full screen) textarea
#
#= provides zen_mode:enter
#= provides zen_mode:leave
#
#= require jquery.scrollTo
#= require dropzone
#= require mousetrap
#= require mousetrap/pause
#
# ### Events
#
# `zen_mode:enter`
#
# Fired when the "Edit in fullscreen" link is clicked.
#
# **Synchronicity** Sync
# **Bubbles** Yes
# **Cancelable** No
# **Target** a.js-zen-enter
#
# `zen_mode:leave`
#
# Fired when the "Leave Fullscreen" link is clicked.
#
# **Synchronicity** Sync
# **Bubbles** Yes
# **Cancelable** No
# **Target** a.js-zen-leave
#
class @ZenMode
constructor: ->
@active_zen_area = null
@active_checkbox = null
@scroll_position = 0
@active_backdrop = null
@active_textarea = null
$(window).scroll =>
if not @active_checkbox
@scroll_position = window.pageYOffset
$('body').on 'click', '.zen-enter-link', (e) =>
$(document).on 'click', '.js-zen-enter', (e) ->
e.preventDefault()
$(e.currentTarget).closest('.zennable').find('.zen-toggle-comment').prop('checked', true).change()
$(e.currentTarget).trigger('zen_mode:enter')
$('body').on 'click', '.zen-leave-link', (e) =>
$(document).on 'click', '.js-zen-leave', (e) ->
e.preventDefault()
$(e.currentTarget).closest('.zennable').find('.zen-toggle-comment').prop('checked', false).change()
$(e.currentTarget).trigger('zen_mode:leave')
$('body').on 'change', '.zen-toggle-comment', (e) =>
checkbox = e.currentTarget
if checkbox.checked
# Disable other keyboard shortcuts in ZEN mode
Mousetrap.pause()
@updateActiveZenArea(checkbox)
else
@exitZenMode()
$(document).on 'zen_mode:enter', (e) =>
@enter(e.target.parentNode)
$(document).on 'zen_mode:leave', (e) =>
@exit()
$(document).on 'keydown', (e) =>
if e.keyCode is 27 # Esc
@exitZenMode()
$(document).on 'keydown', (e) ->
if e.keyCode == 27 # Esc
e.preventDefault()
$(document).trigger('zen_mode:leave')
enter: (backdrop) ->
Mousetrap.pause()
@active_backdrop = $(backdrop)
@active_backdrop.addClass('fullscreen')
@active_textarea = @active_backdrop.find('textarea')
updateActiveZenArea: (checkbox) =>
@active_checkbox = $(checkbox)
@active_checkbox.prop('checked', true)
@active_zen_area = @active_checkbox.parent().find('textarea')
# Prevent a user-resized textarea from persisting to fullscreen
@active_zen_area.removeAttr('style')
@active_zen_area.focus()
@active_textarea.removeAttr('style')
@active_textarea.focus()
exitZenMode: =>
if @active_zen_area isnt null
exit: ->
if @active_textarea
Mousetrap.unpause()
@active_checkbox.prop('checked', false)
@active_zen_area = null
@active_checkbox = null
@restoreScroll(@scroll_position)
# Enable dropzone when leaving ZEN mode
@active_textarea.closest('.zen-backdrop').removeClass('fullscreen')
@scrollTo(@active_textarea)
@active_textarea = null
@active_backdrop = null
Dropzone.forElement('.div-dropzone').enable()
restoreScroll: (y) ->
window.scrollTo(window.pageXOffset, y)
scrollTo: (zen_area) ->
$.scrollTo(zen_area, 0, offset: -150)

View file

@ -1,9 +1,5 @@
.zennable {
.zen-toggle-comment {
display: none;
}
.zen-enter-link {
a.js-zen-enter {
color: $gl-gray;
position: absolute;
top: 0px;
@ -11,7 +7,7 @@
line-height: 40px;
}
.zen-leave-link {
a.js-zen-leave {
display: none;
color: $gl-text-color;
position: absolute;
@ -25,62 +21,41 @@
}
}
// Hide the Enter link when we're in Zen mode
input:checked ~ .zen-backdrop .zen-enter-link {
display: none;
}
.zen-backdrop {
&.fullscreen {
background-color: white;
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 1031;
// Show the Leave link when we're in Zen mode
input:checked ~ .zen-backdrop .zen-leave-link {
display: block;
position: absolute;
top: 0;
}
textarea {
border: none;
box-shadow: none;
border-radius: 0;
color: #000;
font-size: 20px;
line-height: 26px;
padding: 30px;
display: block;
outline: none;
resize: none;
height: 100vh;
max-width: 900px;
margin: 0 auto;
}
input:checked ~ .zen-backdrop {
background-color: white;
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 1031;
a.js-zen-enter {
display: none;
}
textarea {
border: none;
box-shadow: none;
border-radius: 0;
color: #000;
font-size: 20px;
line-height: 26px;
padding: 30px;
display: block;
outline: none;
resize: none;
height: 100vh;
max-width: 900px;
margin: 0 auto;
a.js-zen-leave {
display: block;
position: absolute;
top: 0;
}
}
}
// Make the color of the placeholder text in the Zenned-out textarea darker,
// so it becomes visible
input:checked ~ .zen-backdrop textarea::-webkit-input-placeholder {
color: #A8A8A8;
}
input:checked ~ .zen-backdrop textarea:-moz-placeholder {
color: #A8A8A8;
opacity: 1;
}
input:checked ~ .zen-backdrop textarea::-moz-placeholder {
color: #A8A8A8;
opacity: 1;
}
input:checked ~ .zen-backdrop textarea:-ms-input-placeholder {
color: #A8A8A8;
}
}

View file

@ -1,13 +1,12 @@
.zennable
%input#zen-toggle-comment.zen-toggle-comment(tabindex="-1" type="checkbox")
.zen-backdrop
- classes << ' js-gfm-input markdown-area'
- if defined?(f) && f
= f.text_area attr, class: classes, placeholder: ''
= f.text_area attr, class: classes
- else
= text_area_tag attr, nil, class: classes, placeholder: ''
%a.zen-enter-link(tabindex="-1" href="#")
= text_area_tag attr, nil, class: classes
%a.js-zen-enter(tabindex="-1" href="#")
= icon('expand')
Edit in fullscreen
%a.zen-leave-link(href="#")
%a.js-zen-leave(tabindex="-1" href="#")
= icon('compress')

View file

@ -1,9 +1,8 @@
.zennable
%input#zen-toggle-comment.zen-toggle-comment{ tabindex: '-1', type: 'checkbox' }
.zen-backdrop
%textarea#note_note.js-gfm-input.markdown-area{placeholder: 'Leave a comment'}
%a.zen-enter-link{tabindex: '-1'}
%textarea#note_note.js-gfm-input.markdown-area
%a.js-zen-enter(tabindex="-1" href="#")
%i.fa.fa-expand
Edit in fullscreen
%a.zen-leave-link
Edit in fullscreen
%a.js-zen-leave(tabindex="-1" href="#")
%i.fa.fa-compress

View file

@ -15,14 +15,6 @@ describe 'ZenMode', ->
# Set this manually because we can't actually scroll the window
@zen.scroll_position = 456
# Ohmmmmmmm
enterZen = ->
$('.zen-toggle-comment').prop('checked', true).trigger('change')
# Wh- what was that?!
exitZen = ->
$('.zen-toggle-comment').prop('checked', false).trigger('change')
describe 'on enter', ->
it 'pauses Mousetrap', ->
spyOn(Mousetrap, 'pause')
@ -35,16 +27,14 @@ describe 'ZenMode', ->
expect('textarea').not.toHaveAttr('style')
describe 'in use', ->
beforeEach ->
enterZen()
beforeEach -> enterZen()
it 'exits on Escape', ->
$(document).trigger(jQuery.Event('keydown', {keyCode: 27}))
expect($('.zen-toggle-comment').prop('checked')).toBe(false)
escapeKeydown()
expect($('.zen-backdrop')).not.toHaveClass('fullscreen')
describe 'on exit', ->
beforeEach ->
enterZen()
beforeEach -> enterZen()
it 'unpauses Mousetrap', ->
spyOn(Mousetrap, 'unpause')
@ -52,6 +42,10 @@ describe 'ZenMode', ->
expect(Mousetrap.unpause).toHaveBeenCalled()
it 'restores the scroll position', ->
spyOn(@zen, 'restoreScroll')
spyOn(@zen, 'scrollTo')
exitZen()
expect(@zen.restoreScroll).toHaveBeenCalledWith(456)
expect(@zen.scrollTo).toHaveBeenCalled()
enterZen = -> $('a.js-zen-enter').click() # Ohmmmmmmm
exitZen = -> $('a.js-zen-leave').click()
escapeKeydown = -> $('textarea').trigger($.Event('keydown', {keyCode: 27}))