Improved navigation by keyboard.

This commit is contained in:
Martin Edenhofer 2016-03-29 03:10:24 +02:00
parent a70377558f
commit 6c7d9bcb4d
3 changed files with 123 additions and 93 deletions

View file

@ -9,13 +9,16 @@ class App.UserOrganizationAutocompletion extends App.Controller
'focus input': 'open' 'focus input': 'open'
'click': 'stopPropagation' 'click': 'stopPropagation'
elements:
'.recipientList': 'recipientList'
constructor: (params) -> constructor: (params) ->
super super
@key = Math.floor( Math.random() * 999999 ).toString() @key = Math.floor( Math.random() * 999999 ).toString()
if !@attribute.source if !@attribute.source
@attribute.source = @apiPath + '/search/user-organization' @attribute.source = "#{@apiPath}/search/user-organization"
@build() @build()
# set current value # set current value
@ -31,13 +34,14 @@ class App.UserOrganizationAutocompletion extends App.Controller
open: => open: =>
@clearDelay('close') @clearDelay('close')
@el.addClass('open') @el.addClass('open')
$(window).on 'click.UserOrganizationAutocompletion', @close $(window).on 'click.UserOrganizationAutocompletion', @close
$(window).on 'keydown.UserOrganizationAutocompletion', @navigateByKeyboard
close: => close: =>
$(window).off 'keydown.UserOrganizationAutocompletion'
execute = => execute = =>
@el.removeClass('open') @el.removeClass('open')
@delay(execute, 200, 'close') @delay(execute, 50, 'close')
$(window).off 'click.UserOrganizationAutocompletion' $(window).off 'click.UserOrganizationAutocompletion'
@ -49,7 +53,7 @@ class App.UserOrganizationAutocompletion extends App.Controller
@close() @close()
setUser: (userId) => setUser: (userId) =>
@el.find('[name="' + @attribute.name + '"]').val( userId ).trigger('change') @el.find('[name="' + @attribute.name + '"]').val(userId).trigger('change')
executeCallback: => executeCallback: =>
userId = @el.find('[name="' + @attribute.name + '"]').val() userId = @el.find('[name="' + @attribute.name + '"]').val()
@ -59,65 +63,23 @@ class App.UserOrganizationAutocompletion extends App.Controller
name = user.displayName() name = user.displayName()
if user.email if user.email
name += " <#{user.email}>" name += " <#{user.email}>"
@el.find('[name="' + @attribute.name + '_completion"]').val( name ).trigger('change') @el.find('[name="' + @attribute.name + '_completion"]').val(name).trigger('change')
if @callback if @callback
@callback(userId) @callback(userId)
buildOrganizationItem: (organization) -> navigateByKeyboard: (e) =>
App.view('generic/user_search/item_organization')(
organization: organization
)
buildOrganizationMembers: (organization) =>
organizationMemebers = $( App.view('generic/user_search/item_organization_members')(
organization: organization
) )
for userId in organization.member_ids
user = App.User.fullLocal(userId)
organizationMemebers.append( @buildUserItem(user) )
buildUserItem: (user) ->
App.view('generic/user_search/item_user')(
user: user
)
buildUserNew: ->
App.view('generic/user_search/new_user')()
build: =>
@el.html App.view('generic/user_search/input')(
attribute: @attribute
)
if !@attribute.disableCreateUser
@el.find('.recipientList').append( @buildUserNew() )
@el.find('[name="' + @attribute.name + '"]').on(
'change',
(e) =>
@executeCallback()
)
# navigate in result list
@el.find('[name="' + @attribute.name + '_completion"]').on(
'keydown',
(e) =>
item = $(e.target).val().trim()
#@log('CC', e.keyCode, item)
# clean input field on ESC # clean input field on ESC
if e.keyCode is 27 if e.keyCode is 27
# if org member selection is shown, go back to member list # if org member selection is shown, go back to member list
if @$('.recipientList-backClickArea').is(':visible') if !@recipientList.hasClass('is-shown')
@$('.recipientList-backClickArea').click() @hideOrganizationMembers()
return return
# empty user selection and close # empty user selection and close
$(e.target).val('') $(e.target).val('').trigger('change')
item = ''
@close()
# if tab / close recipientList # if tab / close recipientList
if e.keyCode is 9 if e.keyCode is 9
@ -133,42 +95,103 @@ class App.UserOrganizationAutocompletion extends App.Controller
# up / select upper item # up / select upper item
if e.keyCode is 38 if e.keyCode is 38
e.preventDefault() e.preventDefault()
recipientList = @$('.recipientList') if @recipientList.hasClass('is-shown')
if recipientList.find('li.is-active').length is 0 if @recipientList.find('li.is-active').length is 0
recipientList.find('li').last().addClass('is-active') @recipientList.find('li').last().addClass('is-active')
else else
if recipientList.find('li.is-active').prev().length isnt 0 if @recipientList.find('li.is-active').prev().length isnt 0
recipientList.find('li.is-active').removeClass('is-active').prev().addClass('is-active') @recipientList.find('li.is-active').removeClass('is-active').prev().addClass('is-active')
return
recipientListOrgMemeber = @$('.recipientList-organizationMembers').not('.hide')
if recipientListOrgMemeber.not('.hide').find('li.is-active').length is 0
recipientListOrgMemeber.not('.hide').find('li').last().addClass('is-active')
else
if recipientListOrgMemeber.not('.hide').find('li.is-active').prev().length isnt 0
recipientListOrgMemeber.not('.hide').find('li.is-active').removeClass('is-active').prev().addClass('is-active')
return return
# down / select lower item # down / select lower item
if e.keyCode is 40 if e.keyCode is 40
e.preventDefault() e.preventDefault()
recipientList = @$('.recipientList') if @recipientList.hasClass('is-shown')
if recipientList.find('li.is-active').length is 0 if @recipientList.find('li.is-active').length is 0
recipientList.find('li').first().addClass('is-active') @recipientList.find('li').first().addClass('is-active')
else else
if recipientList.find('li.is-active').next().length isnt 0 if @recipientList.find('li.is-active').next().length isnt 0
recipientList.find('li.is-active').removeClass('is-active').next().addClass('is-active') @recipientList.find('li.is-active').removeClass('is-active').next().addClass('is-active')
return
recipientListOrgMemeber = @$('.recipientList-organizationMembers').not('.hide')
if recipientListOrgMemeber.not('.hide').find('li.is-active').length is 0
recipientListOrgMemeber.find('li').first().addClass('is-active')
else
if recipientListOrgMemeber.not('.hide').find('li.is-active').next().length isnt 0
recipientListOrgMemeber.not('.hide').find('li.is-active').removeClass('is-active').next().addClass('is-active')
return return
# enter / take item # enter / take item
if e.keyCode is 13 if e.keyCode is 13
e.preventDefault() e.preventDefault()
e.stopPropagation() e.stopPropagation()
userId = @$('.recipientList').find('li.is-active').data('user-id')
if !userId # nav by org member selection
organizationId = @$('.recipientList').find('li.is-active').data('organization-id') if !@recipientList.hasClass('is-shown')
if !organizationId recipientListOrganizationMembers = @$('.recipientList-organizationMembers').not('.hide')
if recipientListOrganizationMembers.find('.js-back.is-active').get(0)
@hideOrganizationMembers()
return return
@showOrganizationMembers(undefined, @$('.recipientList').find('li.is-active')) userId = recipientListOrganizationMembers.find('li.is-active').data('user-id')
return if !userId
@setUser(userId)
@close()
return return
# nav by user list selection
userId = @recipientList.find('li.is-active').data('user-id')
if userId
if userId is 'new' if userId is 'new'
@newUser() @newUser()
else else
@setUser(userId) @setUser(userId)
@close() @close()
return return
organizationId = @recipientList.find('li.is-active').data('organization-id')
return if !organizationId
@showOrganizationMembers(undefined, @recipientList.find('li.is-active'))
buildOrganizationItem: (organization) ->
App.view('generic/user_search/item_organization')(
organization: organization
)
buildOrganizationMembers: (organization) =>
organizationMemebers = $( App.view('generic/user_search/item_organization_members')(
organization: organization
) )
for userId in organization.member_ids
user = App.User.fullLocal(userId)
organizationMemebers.append(@buildUserItem(user))
buildUserItem: (user) ->
App.view('generic/user_search/item_user')(
user: user
)
buildUserNew: ->
App.view('generic/user_search/new_user')()
build: =>
@html App.view('generic/user_search/input')(
attribute: @attribute
)
if !@attribute.disableCreateUser
@recipientList.append(@buildUserNew())
@el.find('[name="' + @attribute.name + '"]').on(
'change',
(e) =>
@executeCallback()
) )
# start search # start search
@ -180,10 +203,12 @@ class App.UserOrganizationAutocompletion extends App.Controller
return if @searchTerm is term return if @searchTerm is term
@searchTerm = term @searchTerm = term
@hideOrganizationMembers()
# hide dropdown # hide dropdown
if !term && !@attribute.disableCreateUser if !term && !@attribute.disableCreateUser
@emptyResultList() @emptyResultList()
@$('.recipientList').append(@buildUserNew()) @recipientList.append(@buildUserNew())
# show dropdown # show dropdown
if term && ( !@attribute.minLengt || @attribute.minLengt <= term.length ) if term && ( !@attribute.minLengt || @attribute.minLengt <= term.length )
@ -193,7 +218,7 @@ class App.UserOrganizationAutocompletion extends App.Controller
searchUser: (term) => searchUser: (term) =>
@ajax( @ajax(
id: 'searchUser' + @key id: "searchUser#{@key}"
type: 'GET' type: 'GET'
url: @attribute.source url: @attribute.source
data: data:
@ -211,7 +236,7 @@ class App.UserOrganizationAutocompletion extends App.Controller
# organization # organization
if item.type is 'Organization' if item.type is 'Organization'
organization = App.Organization.fullLocal(item.id) organization = App.Organization.fullLocal(item.id)
@$('.recipientList').append(@buildOrganizationItem(organization)) @recipientList.append(@buildOrganizationItem(organization))
# users of organization # users of organization
if organization.member_ids if organization.member_ids
@ -220,14 +245,14 @@ class App.UserOrganizationAutocompletion extends App.Controller
# users # users
if item.type is 'User' if item.type is 'User'
user = App.User.fullLocal(item.id) user = App.User.fullLocal(item.id)
@$('.recipientList').append(@buildUserItem(user)) @recipientList.append(@buildUserItem(user))
if !@attribute.disableCreateUser if !@attribute.disableCreateUser
@$('.recipientList').append(@buildUserNew()) @recipientList.append(@buildUserNew())
) )
emptyResultList: => emptyResultList: =>
@$('.recipientList').empty() @recipientList.empty()
@$('.recipientList-organizationMembers').remove() @$('.recipientList-organizationMembers').remove()
showOrganizationMembers: (e,listEntry) => showOrganizationMembers: (e,listEntry) =>
@ -237,11 +262,14 @@ class App.UserOrganizationAutocompletion extends App.Controller
organizationId = listEntry.data('organization-id') organizationId = listEntry.data('organization-id')
@recipientList = @$('.recipientList') @organizationList = @$("[organization-id=#{ organizationId }]")
@organizationList = @$("##{ organizationId }")
return if !@organizationList.get(0)
@recipientList.removeClass('is-shown')
@$('.recipientList-organizationMembers').addClass('is-shown')
# move organization-list to the right and slide it in # move organization-list to the right and slide it in
$.Velocity.hook(@organizationList, 'translateX', '100%') $.Velocity.hook(@organizationList, 'translateX', '100%')
@organizationList.removeClass('hide') @organizationList.removeClass('hide')
@ -262,6 +290,9 @@ class App.UserOrganizationAutocompletion extends App.Controller
hideOrganizationMembers: (e) => hideOrganizationMembers: (e) =>
e && e.stopPropagation() e && e.stopPropagation()
@recipientList.addClass('is-shown')
@$('.recipientList-organizationMembers').removeClass('is-shown')
return if !@organizationList return if !@organizationList
# fade list back in # fade list back in
@ -272,7 +303,6 @@ class App.UserOrganizationAutocompletion extends App.Controller
speed: 300 speed: 300
# reset list height # reset list height
@recipientList.height('') @recipientList.height('')
# slide out organization-list and hide it # slide out organization-list and hide it

View file

@ -1,4 +1,4 @@
<ul class="recipientList-organizationMembers hide" id="<%- @organization.id %>"> <ul class="recipientList-organizationMembers hide" organization-id="<%- @organization.id %>">
<li class="recipientList-controls js-back"> <li class="recipientList-controls js-back">
<div class="btn btn--action btn--onDark"> <div class="btn btn--action btn--onDark">
<%- @Icon('arrow-left') %> <%- @Icon('arrow-left') %>

View file

@ -5879,7 +5879,7 @@ footer {
.recipientList-controls:hover { .recipientList-controls:hover {
@extend .u-clickable; @extend .u-clickable;
padding: 0 10px !important; padding: 0 10px !important;
background: hsl(206,7%,28%) !important; background: hsl(206,7%,28%);
& + li { & + li {
box-shadow: 0 1px rgba(255,255,255,.13) inset; box-shadow: 0 1px rgba(255,255,255,.13) inset;