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:
parent
6d95444108
commit
89ca4f04f6
5 changed files with 114 additions and 123 deletions
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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}))
|
||||
|
|
Loading…
Reference in a new issue