app/scripts/ui-lib/widgets/toolbar.coffee

Toolbar

The Toolbar class provides a simple toolbar widget.

author: Julien Ramboz version: 1.0 references: WAI-ARIA toolbar role, AOL's Tool Bar style guide usage: Toolbar examples

AMD loader

Try loading as AMD module or fall back to default loading

((widget) -> if typeof define is "function" and define.amd define ["jslib", "core", "widget"], widget else widget @$, _, _.AbstractWidget ) toolbar = ($, _, AbstractWidget) -> "use strict"

Widget

The actual widget class

class Toolbar extends AbstractWidget

Default options for the widget

@defaultOptions:
  • groups: a selector pointing to the groups
groups: ""
  • items: a selector pointing to the items
items: ""

Initialisation

Initializer function.

initialize: (options) -> super options @groups = @findGroups() @items = @findItems()

Accessibility markup

Add aria attributes

aria: () -> super() @element.attr("role", "toolbar") $label = @element.children().first() if $label.length and $label.attr("data-role") is "label" if not $label.attr("id") $label .attr("role", "presentation") .attr("id", _.getGUID("toolbar-label-")) @element.attr("aria-labelledby", $label.attr("id")) @groups.attr("role", "group").each () -> $group = $(this) $label = $group.children().first() if $label.length and $label.attr("data-role") is "label" $label.attr("role", "presentation") if not $label.attr("id") $label.attr("id", _.getGUID("group-label-")) $group.attr("aria-labelledby", $label.attr("id")) @items.filter( () -> not $(this).is("button,[role='button']")).attr "role": "button" "tabindex": -1 @items.filter( () -> $(this).find("[data-widget='menu']").length).attr "aria-haspopup": true @items.first().attr("tabindex", 0)

Event handling

Attach evenets to the widget

bindEvents: () -> @handleKeys(@items) @items.on "click", (e) => if e.target.getAttribute("aria-disabled") is "true" return e.preventDefault() $target = $(e.target) if not e.target.getAttribute("aria-haspopup") $target = $target.parents("[aria-haspopup='true']") if @items.index($target) > -1 @currentItem = $target @selectItem($target, e) @items.on "focus", (e) => $target = $(e.target) if @items.index($target) > -1 @currentItem = $target else $item = @items.filter(() -> $(this).find($target).length) @currentItem = $item if $item.length

Select the previous item

selectPreviousItem: (e) -> index = @items.index(@currentItem or @items.first()) max = @items.length loop $item = @items.eq(--index) break if $item.get(0).getAttribute("aria-hidden") isnt "true" and not $item.parents("[role='group'][aria-hidden='true']").length return if --max < 0 @selectItem($item, e)

Select the next item

selectNextItem: (e) -> index = @items.index(@currentItem or @items.last()) max = @items.length loop $item = @items.eq(++index % @items.length) break if $item.get(0).getAttribute("aria-hidden") isnt "true" and not $item.parents("[role='group'][aria-hidden='true']").length return if --max < 0 @selectItem($item, e)

Select the specified item

selectItem: ($item, e) -> if isNaN($item) return if not $item else $item = @items.eq($item) @items.removeAttr("tabindex") @currentItem = $item @currentItem.attr("tabindex", 0)

Close menus

closeAll: () -> @element.find("[role='menu']")["#{_.namespace}menu"]("closeAll")

Structure discovery

Find the widget structure using the specified configuration

Toolbar groups

Find the toolbar groups, specified in one of the following ways:

  • elements with role="group"
  • using groups option, specified as a selector
findGroups: () -> $groups = @element.find("[role='group']") return $groups if $groups.length if @options.groups $groups = $(@options.groups, @element) else $groups = $() $groups.filter () -> not $(this).is("[role='presentation'],[data-role='label']")

Toolbar items

Find the toolbar items, specified in one of the following ways:

  • using items option, specified as a selector
  • descendants of groups
  • descendants of the element
findItems: () -> if @options.items $items = $(@options.items, @element) else if @groups.length $items = @groups.children() else $items = @element.children() $items.filter () -> not $(this).is("[role='presentation'],[data-role='label']")

Get the toggle for a specific item

getToggle: ($item) -> $item = @currentItem if not $item return if $item.get(0).getAttribute("aria-haspopup") isnt "true" return $item if $item.attr("aria-controls") return $item.find("[aria-controls]")

Get the submenu for a specific item

getSubmenu: ($item) -> $item = @currentItem if not $item return if $item.get(0).getAttribute("aria-haspopup") isnt "true" $item.children("[role='menu']")

Key handling

Handle left key press

keyLeft: (e) -> e.preventDefault() e.stopPropagation() if @currentItem.get(0).getAttribute("aria-haspopup") is "true" or @currentItem.get(0).getAttribute("aria-expanded") is "true" @closeAll() @selectPreviousItem(e).focus()

Handle right key press

keyRight: (e) -> e.preventDefault() e.stopPropagation() if @currentItem.get(0).getAttribute("aria-haspopup") is "true" or @currentItem.get(0).getAttribute("aria-expanded") is "true" @closeAll() @selectNextItem(e).focus()

Handle down key press

keyDown: (e) -> $parent = $(e.target).parent(".nav-item") @selectItem($parent, e) if $parent.length return if @currentItem.get(0).getAttribute("aria-disabled") is "true" if @currentItem.get(0).getAttribute("aria-haspopup") is "true" e.preventDefault() e.stopPropagation() $submenu = @getSubmenu() if $submenu.get(0).getAttribute("aria-hidden") is "true" $toggle = @getToggle() _.getInstance($toggle, ["toggle","collapse"]).show(e)

Cleanup

Cleanup the widget and remove remaining references

destructor: () -> @items.removeAttr "tabindex" @items = null @element.removeAttr "role" super()

Installation

Install the widget into the JS library

Toolbar.install("Toolbar", () -> $("[data-widget='toolbar']")["#{_.namespace}toolbar"]() )