gl_dropdown.js.coffee 6.03 KB
Newer Older
Phil Hughes's avatar
Phil Hughes committed
1 2 3
class GitLabDropdownFilter
  BLUR_KEYCODES = [27, 40]

Phil Hughes's avatar
Phil Hughes committed
4
  constructor: (@dropdown, @remote, @query, @data, @callback) ->
5
    @input = @dropdown.find(".dropdown-input .dropdown-input-field")
Phil Hughes's avatar
Phil Hughes committed
6 7

    # Key events
Phil Hughes's avatar
Phil Hughes committed
8
    timeout = ""
Phil Hughes's avatar
Phil Hughes committed
9
    @input.on "keyup", (e) =>
Phil Hughes's avatar
Phil Hughes committed
10 11 12 13
      clearTimeout timeout
      timeout = setTimeout =>
        blur_field = @shouldBlur e.keyCode
        search_text = @input.val()
Phil Hughes's avatar
Phil Hughes committed
14

Phil Hughes's avatar
Phil Hughes committed
15 16
        if blur_field
          @input.blur()
Phil Hughes's avatar
Phil Hughes committed
17

Phil Hughes's avatar
Phil Hughes committed
18 19 20 21 22 23
        if @remote
          @query search_text, (data) =>
            @callback(data)
        else
          @filter search_text
      , 250
Phil Hughes's avatar
Phil Hughes committed
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44

  shouldBlur: (keyCode) ->
    return BLUR_KEYCODES.indexOf(keyCode) >= 0

  filter: (search_text) ->
    data = @data()
    results = if search_text isnt "" then data.search(search_text) else data.list

    @callback results

class GitLabDropdownRemote
  constructor: (@dataEndpoint, @options) ->

  execute: ->
    if typeof @dataEndpoint is "string"
      @fetchData()
    else if typeof @dataEndpoint is "function"
      if @options.beforeSend
        @options.beforeSend()

      # Fetch the data by calling the data funcfion
Phil Hughes's avatar
Phil Hughes committed
45
      @dataEndpoint "", (data) =>
Phil Hughes's avatar
Phil Hughes committed
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
        if @options.success
          @options.success(data)

        if @options.beforeSend
          @options.beforeSend()

  # Fetch the data through ajax if the data is a string
  fetchData: ->
    $.ajax(
      url: @dataEndpoint,
      dataType: @options.dataType,
      beforeSend: =>
        if @options.beforeSend
          @options.beforeSend()
      success: (data) =>
        if @options.success
          @options.success(data)
    )

class GitLabDropdown
  LOADING_CLASS = "is-loading"
67
  PAGE_TWO_CLASS = "is-page-two"
Phil Hughes's avatar
Phil Hughes committed
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93

  constructor: (@el, @options) ->
    self = @
    @dropdown = $(@el).parent()
    search_fields = if @options.search then @options.search.fields else [];

    if @options.data
      # Remote data
      @remote = new GitLabDropdownRemote @options.data, {
        dataType: @options.dataType,
        beforeSend: @toggleLoading.bind(@)
        success: (data) =>
          @fullData = data
          dataToPrase = @fullData

          if @options.filterable
            @fullData = new Fuse data, {
              keys: search_fields
            }
            dataToPrase = @fullData.list

          @parseData dataToPrase
      }

    # Init filiterable
    if @options.filterable
Phil Hughes's avatar
Phil Hughes committed
94
      @filter = new GitLabDropdownFilter @dropdown, @options.filterRemote, @options.data, =>
Phil Hughes's avatar
Phil Hughes committed
95 96 97 98 99
        return @fullData
      , (data) =>
        @parseData data

    # Event listeners
100 101 102 103 104 105 106 107 108
    @dropdown.on "shown.bs.dropdown", @opened
    @dropdown.on "hidden.bs.dropdown", @hidden

    if @dropdown.find(".dropdown-toggle-page").length
      @dropdown.find(".dropdown-toggle-page, .dropdown-menu-back").on "click", (e) =>
        e.preventDefault()
        e.stopPropagation()

        @togglePage()
Phil Hughes's avatar
Phil Hughes committed
109 110

    if @options.selectable
111
      selector = ".dropdown-content a"
112 113

      if @dropdown.find(".dropdown-toggle-page").length
114
        selector = ".dropdown-page-one .dropdown-content a"
115 116

      @dropdown.on "click", selector, (e) ->
Phil Hughes's avatar
Phil Hughes committed
117 118 119 120 121 122 123 124
        self.rowClicked $(@)

        if self.options.clicked
          self.options.clicked()

  toggleLoading: ->
    $('.dropdown-menu', @dropdown).toggleClass LOADING_CLASS

125 126 127 128 129 130 131 132 133
  togglePage: ->
    menu = $('.dropdown-menu', @dropdown)

    if menu.hasClass(PAGE_TWO_CLASS)
      if @remote
        @remote.execute()

    menu.toggleClass PAGE_TWO_CLASS

Phil Hughes's avatar
Phil Hughes committed
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
  parseData: (data) ->
    @renderedData = data

    # Render each row
    html = $.map data, (obj) =>
      return @renderItem(obj)

    if @options.filterable and data.length is 0
      # render no matching results
      html = [@noResults()]

    # Render the full menu
    full_html = @renderMenu(html.join(""))

    @appendMenu(full_html)

  opened: =>
    if @remote
      @remote.execute()

Phil Hughes's avatar
Phil Hughes committed
154 155 156 157 158 159 160
    if @options.filterable
      @dropdown.find(".dropdown-input-field").focus()

  hidden: =>
    if @options.filterable
      @dropdown.find(".dropdown-input-field").blur().val("")

161 162 163 164
    if @dropdown.find(".dropdown-toggle-page").length
      $('.dropdown-menu', @dropdown).removeClass PAGE_TWO_CLASS


Phil Hughes's avatar
Phil Hughes committed
165 166 167 168 169 170 171 172 173 174 175 176 177
  # Render the full menu
  renderMenu: (html) ->
    menu_html = ""

    if @options.renderMenu
      menu_html = @options.renderMenu(html)
    else
      menu_html = "<ul>#{html}</ul>"

    return menu_html

  # Append the menu into the dropdown
  appendMenu: (html) ->
178 179 180 181 182
    selector = '.dropdown-content'
    if @dropdown.find(".dropdown-toggle-page").length
      selector = ".dropdown-page-one .dropdown-content"

    $(selector, @dropdown).html html
Phil Hughes's avatar
Phil Hughes committed
183 184 185 186 187

  # Render the row
  renderItem: (data) ->
    html = ""

188 189
    return "<li class='divider'></li>" if data is "divider"

Phil Hughes's avatar
Phil Hughes committed
190 191 192 193 194
    if @options.renderRow
      # Call the render function
      html = @options.renderRow(data)
    else
      selected = if @options.isSelected then @options.isSelected(data) else false
Phil Hughes's avatar
Phil Hughes committed
195
      url = if @options.url then @options.url(data) else "#"
Phil Hughes's avatar
Phil Hughes committed
196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
      text = if @options.text then @options.text(data) else ""
      cssClass = "";

      if selected
        cssClass = "is-active"

      html = "<li>"
      html += "<a href='#{url}' class='#{cssClass}'>"
      html += text
      html += "</a>"
      html += "</li>"

    return html

  noResults: ->
    html = "<li>"
    html += "<a href='#' class='is-focused'>"
    html += "No matching results."
    html += "</a>"
    html += "</li>"

  rowClicked: (el) ->
    fieldName = @options.fieldName
    selectedIndex = el.parent().index()
Phil Hughes's avatar
Phil Hughes committed
220 221 222
    if @renderedData
      selectedObject = @renderedData[selectedIndex]
    value = if @options.id then @options.id(selectedObject, el) else selectedObject.id
Phil Hughes's avatar
Phil Hughes committed
223 224

    if @options.multiSelect
225
      fieldName = "#{fieldName}[]"
Phil Hughes's avatar
Phil Hughes committed
226 227 228 229 230 231 232
    else
      @dropdown.find('.is-active').removeClass 'is-active'
      @dropdown.parent().find("input[name='#{fieldName}']").remove()

    # Toggle active class for the tick mark
    el.toggleClass "is-active"

Phil Hughes's avatar
Phil Hughes committed
233
    if value
234 235 236
      # Create hidden input for form
      input = "<input type='hidden' name='#{fieldName}' value='#{value}' />"
      @dropdown.before input
Phil Hughes's avatar
Phil Hughes committed
237 238 239 240

$.fn.glDropdown = (opts) ->
  return @.each ->
    new GitLabDropdown @, opts