Skip to content

Commit 71a650d

Browse files
committed
revised address book, fixes #5639
added/changed functionality: * search for course sections * browse and select groups/sections/enrollment types (e.g. all teachers) within a course * browse and select enrollment types within a section * changed toggle vs. drilldown input handling (e.g. clicking on an expandable element now always expands it, unless you click on the checkbox) * "Select All" checkbox within groups and enrollment types Change-Id: I13baa9e8b98abc5b5ec6dfb4cfbd1021160f65cc Reviewed-on: https://gerrit.instructure.com/5955 Tested-by: Hudson <hudson@instructure.com> Tested-by: Jon Jensen <jon@instructure.com> Reviewed-by: Zach Wily <zach@instructure.com> Reviewed-by: Zach Pendleton <zachp@instructure.com>
1 parent 6e2f7ac commit 71a650d

9 files changed

Lines changed: 863 additions & 149 deletions

File tree

app/coffeescripts/messages.coffee

Lines changed: 124 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ class TokenSelector
206206
true
207207

208208
new_list: ->
209-
$list = $('<div><ul class="heading"></ul><ul></ul></div>')
209+
$list = $('<div class="list"><ul class="heading"></ul><ul></ul></div>')
210210
$list.find('ul')
211211
.mousemove (e) =>
212212
return if @ui_locked
@@ -231,11 +231,17 @@ class TokenSelector
231231
@expand_selection()
232232
else if @selection_toggleable() and $(e.target).closest('a.toggle').length
233233
@toggle_selection()
234-
else if not @selection.hasClass('expanded')
235-
@toggle_selection(on)
236-
@clear()
237-
@close()
234+
else
235+
if @selection_expanded()
236+
@collapse()
237+
else if @selection_expandable()
238+
@expand_selection()
239+
else
240+
@toggle_selection(on)
241+
@clear()
242+
@close()
238243
@input.focus()
244+
$list.body = $list.find('ul').last()
239245
$list
240246

241247
capture_keydown: (e) ->
@@ -251,12 +257,19 @@ class TokenSelector
251257
@input.remove_last_token()
252258
return true
253259
when 'Tab', 'U+0009', 9
254-
@toggle_selection(on) if @selection and not @selection.hasClass('expanded')
260+
if @selection and (@selection_toggleable() or not @selection_expandable())
261+
@toggle_selection(on)
255262
@clear()
256263
@close()
257264
return true if @selection
258265
when 'Enter', 13
259-
if @selection and not @selection.hasClass('expanded')
266+
if @selection_expanded()
267+
@collapse()
268+
return true
269+
else if @selection_expandable() and not @selection_toggleable()
270+
@expand_selection()
271+
return true
272+
else if @selection
260273
@toggle_selection(on)
261274
@clear()
262275
@close()
@@ -317,7 +330,7 @@ class TokenSelector
317330
@last_applied_query = this_query
318331
@last_search = post_data.search
319332
@clear_loading()
320-
@render_list(@query_cache[this_query], options)
333+
@render_list(@query_cache[this_query], options, post_data)
321334
return
322335

323336
@set_loading()
@@ -328,7 +341,7 @@ class TokenSelector
328341
if JSON.stringify(@prepare_post(options.data ? {})) is this_query # i.e. only if it hasn't subsequently changed (and thus triggered another call)
329342
@last_applied_query = this_query
330343
@last_search = post_data.search
331-
@render_list(data, options) if @menu.is(":visible")
344+
@render_list(data, options, post_data) if @menu.is(":visible")
332345
else
333346
@ui_locked=false
334347
,
@@ -369,8 +382,8 @@ class TokenSelector
369382
selection_expandable: ->
370383
@selection?.hasClass('expandable') ? false
371384

372-
selection_toggleable: ->
373-
@selection?.hasClass('toggleable') ? false
385+
selection_toggleable: ($node=@selection) ->
386+
($node?.hasClass('toggleable') ? false) and not @selection_expanded()
374387

375388
expand_selection: ->
376389
return false unless @selection_expandable() and not @selection_expanded()
@@ -390,19 +403,57 @@ class TokenSelector
390403
@list = $list
391404
@select $selection
392405
@ui_locked = false
393-
# TODO: if any in this list are now selected, we should requery so
394-
# as to remove them
395406

396-
toggle_selection: (state) ->
397-
return false unless state? or @selection_toggleable()
398-
id = @selection.data('id')
399-
state = !@input.has_token(value: id) unless state?
407+
toggle_selection: (state, $node=@selection, toggle_only=false) ->
408+
return false unless state? or @selection_toggleable($node)
409+
id = $node.data('id')
410+
state = !$node.hasClass('on') unless state?
400411
if state
401-
@selection.addClass('on') if @selection_toggleable()
402-
@input.add_token value: id, text: @selection.find('b').text(), no_clear: true, data: @selection.data('user_data')
412+
$node.addClass('on') if @selection_toggleable($node) and not toggle_only
413+
@input.add_token
414+
value: id
415+
text: $node.data('text') ? $node.text()
416+
no_clear: true
417+
data: $node.data('user_data')
403418
else
404-
@selection.removeClass('on')
419+
$node.removeClass('on') unless toggle_only
405420
@input.remove_token value: id
421+
@update_select_all($node) unless toggle_only
422+
423+
update_select_all: ($node, offset=0) ->
424+
select_all_toggled = $node.data('user_data').select_all
425+
$list = if offset then @stack[@stack.length - offset][1] else @list
426+
$select_all = $list.select_all
427+
return unless $select_all
428+
$nodes = $list.body.find('li.toggleable').not($select_all)
429+
if select_all_toggled
430+
if $select_all.hasClass('on')
431+
$nodes.addClass('on').each (i, node) =>
432+
@toggle_selection off, $(node), true
433+
else
434+
$nodes.removeClass('on').each (i, node) =>
435+
@toggle_selection off, $(node), true
436+
else
437+
$on_nodes = $nodes.filter('.on')
438+
if $on_nodes.length < $nodes.length and $select_all.hasClass('on')
439+
$select_all.removeClass('on')
440+
@toggle_selection off, $select_all, true
441+
$on_nodes.each (i, node) =>
442+
@toggle_selection on, $(node), true
443+
else if $on_nodes.length == $nodes.length and not $select_all.hasClass('on')
444+
$select_all.addClass('on')
445+
@toggle_selection on, $select_all, true
446+
$on_nodes.each (i, node) =>
447+
@toggle_selection off, $(node), true
448+
if offset < @stack.length
449+
offset++
450+
$parent_node = @stack[@stack.length - offset][0]
451+
if @selection_toggleable($parent_node)
452+
if $select_all.hasClass('on')
453+
$parent_node.addClass('on')
454+
else
455+
$parent_node.removeClass('on')
456+
@update_select_all($parent_node, offset)
406457

407458
select: ($node, preserve_mode = false) ->
408459
return if $node?[0] is @selection?[0]
@@ -459,25 +510,33 @@ class TokenSelector
459510
clear_loading: ->
460511
@list.find('li').first().loadingImage('remove')
461512

462-
render_list: (data, options={}) ->
513+
render_list: (data, options={}, post_data={}) ->
463514
@open()
464515

465516
if options.expand
466517
$list = @new_list()
467518
else
468519
$list = @list
520+
$list.select_all = null
469521

470522
@selection = null
471523
$uls = $list.find('ul')
472524
$uls.html('')
473525
$heading = $uls.first()
474526
$body = $uls.last()
475527
if data.length
528+
parent = if @stack.length then @stack[@stack.length - 1][0] else null
529+
unless data.prepared
530+
@options.preparer?(post_data, data, parent)
531+
data.prepared = true
532+
476533
for row, i in data
477534
$li = $('<li />').addClass('selectable')
478-
@populate_row($li, row, {level: @stack.length, first: (i is 0), last: (i is data.length - 1)})
535+
@populate_row($li, row, level: @stack.length, first: (i is 0), last: (i is data.length - 1), parent: parent)
536+
$list.select_all = $li if row.select_all
479537
$li.addClass('on') if $li.hasClass('toggleable') and @input.has_token($li.data('id'))
480538
$body.append($li)
539+
$list.body.find('li.toggleable').addClass('on') if $list.select_all?.hasClass?('on') or @stack.length and @stack[@stack.length - 1][0].hasClass?('on')
481540
else
482541
$message = $('<li class="message first last"></li>')
483542
$message.text(@options.messages?.no_results ? '')
@@ -502,13 +561,12 @@ class TokenSelector
502561
@ui_locked = false
503562

504563
prepare_post: (data) ->
505-
post_data = $.extend(data, {search: @input.val()})
564+
post_data = $.extend(data, {search: @input.val()}, @options.base_data ? {})
506565
post_data.exclude = @input.base_exclude.concat(if @stack.length then [] else @input.token_values())
507566
post_data.context = @stack[@stack.length - 1][0].data('id') if @list_expanded()
508567
post_data.per_page ?= @options.limiter?(level: @stack.length)
509568
post_data
510569

511-
512570
# depends on the scrollable ancestor being the first positioned
513571
# ancestor. if it's not, it won't work
514572
$.fn.scrollIntoView = (options = {}) ->
@@ -1509,24 +1567,62 @@ I18n.scoped 'conversations', (I18n) ->
15091567
$context_name = if data.context_name then $('<span />', class: 'context_name').text("(#{context_name})") else ''
15101568
$b = $('<b />')
15111569
$b.text(data.name)
1512-
$span = $('<span />')
1570+
$name = $('<span />', class: 'name')
1571+
$name.append($b, $context_name)
1572+
$span = $('<span />', class: 'details')
15131573
if data.common_courses?
15141574
$span.text(MessageInbox.context_list(courses: data.common_courses, groups: data.common_groups))
15151575
else if data.type and data.user_count?
15161576
$span.text(I18n.t('people_count', 'person', {count: data.user_count}))
1517-
$node.append($b, $context_name, $span)
1577+
else if data.item_count?
1578+
if data.id.match(/_groups$/)
1579+
$span.text(I18n.t('groups_count', 'group', {count: data.item_count}))
1580+
else if data.id.match(/_sections$/)
1581+
$span.text(I18n.t('sections_count', 'section', {count: data.item_count}))
1582+
$node.append($name, $span)
15181583
$node.attr('title', data.name)
1584+
text = data.name
1585+
if options.parent
1586+
if data.select_all and data.no_expand # "Select All", e.g. course_123_all -> "Spanish 101: Everyone"
1587+
text = options.parent.data('text')
1588+
else if (data.id + '').match(/_\d+_/) # e.g. course_123_teachers -> "Spanish 101: Teachers"
1589+
text = I18n.beforeLabel(options.parent.data('text')) + " " + text
1590+
$node.data('text', text)
15191591
$node.data('id', data.id)
15201592
$node.data('user_data', data)
15211593
$node.addClass(if data.type then data.type else 'user')
15221594
if options.level > 0
15231595
$node.prepend('<a class="toggle"><i></i></a>')
1524-
$node.addClass('toggleable')
1525-
if data.type == 'context'
1596+
$node.addClass('toggleable') unless data.item_count # can't toggle synthetic contexts, e.g. "Student Groups"
1597+
if data.type == 'context' and not data.no_expand
15261598
$node.prepend('<a class="expand"><i></i></a>')
15271599
$node.addClass('expandable')
15281600
limiter: (options) ->
15291601
if options.level > 0 then -1 else 5
1602+
preparer: (post_data, data, parent) ->
1603+
context = post_data.context
1604+
if not post_data.search and context and data.length > 1
1605+
if context.match(/^(course|section)_\d+$/)
1606+
# i.e. we are listing synthetic contexts under a course or section
1607+
data.unshift
1608+
id: "#{context}_all"
1609+
name: I18n.t('enrollments_everyone', "Everyone")
1610+
user_count: parent.data('user_data').user_count
1611+
type: 'context'
1612+
avatar_url: parent.data('user_data').avatar_url
1613+
select_all: true
1614+
else if context.match(/^((course|section)_\d+_.*|group_\d+)$/) and not context.match(/^course_\d+_(groups|sections)$/)
1615+
# i.e. we are listing all users in a group or synthetic context
1616+
data.unshift
1617+
id: context
1618+
name: I18n.t('select_all', "Select All")
1619+
user_count: parent.data('user_data').user_count
1620+
type: 'context'
1621+
avatar_url: parent.data('user_data').avatar_url
1622+
select_all: true
1623+
no_expand: true # just a magic select-all checkbox, you can't drill into it
1624+
base_data:
1625+
synthetic_contexts: 1
15301626
browser:
15311627
data:
15321628
per_page: -1

0 commit comments

Comments
 (0)