summaryrefslogtreecommitdiff
path: root/templates/assets/bootstrap-material-design/js/drawer.js
blob: ef0586ed77aa2a0b2709704e50fe31ffe38ca0ec (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
import BaseLayout from './baseLayout'

const Drawer = (($) => {

  /**
   * ------------------------------------------------------------------------
   * Constants
   * ------------------------------------------------------------------------
   */
  const NAME = 'drawer'
  const DATA_KEY = `bmd.${NAME}`
  const JQUERY_NAME = `bmd${NAME.charAt(0).toUpperCase() + NAME.slice(1)}`
  const JQUERY_NO_CONFLICT = $.fn[JQUERY_NAME]

  const Keycodes = {
    ESCAPE: 27
    //ENTER: 13,
    //SPACE: 32
  }

  const ClassName = {
    IN: 'in',
    DRAWER_IN: `bmd-drawer-in`,
    DRAWER_OUT: `bmd-drawer-out`,
    DRAWER: 'bmd-layout-drawer',
    CONTAINER: 'bmd-layout-container'
  }

  const Default = {
    focusSelector: `a, button, input`
  }

  /**
   * ------------------------------------------------------------------------
   * Class Definition
   * ------------------------------------------------------------------------
   */
  class Drawer extends BaseLayout {

    // $element is expected to be the trigger
    //  i.e. <button class="btn bmd-btn-icon" for="search" data-toggle="drawer" data-target="#my-side-nav-drawer" aria-expanded="false" aria-controls="my-side-nav-drawer">
    constructor($element, config) {
      super($element, $.extend(true, {}, Default, config))

      this.$toggles = $(`[data-toggle="drawer"][href="#${this.$element[0].id}"], [data-toggle="drawer"][data-target="#${this.$element[0].id}"]`)

      this._addAria()

      // click or escape on the backdrop closes the drawer
      this.$backdrop.keydown((ev) => {
        if (ev.which === Keycodes.ESCAPE) {
          this.hide()
        }
      }).click(() => {
        this.hide()
      })

      // escape on the drawer closes it
      this.$element.keydown((ev) => {
        if (ev.which === Keycodes.ESCAPE) {
          this.hide()
        }
      })

      // any toggle button clicks
      this.$toggles.click(() => {
        this.toggle()
      })
    }

    dispose() {
      super.dispose(DATA_KEY)
      this.$toggles = null
    }

    toggle() {
      if (this._isOpen()) {
        this.hide()
      } else {
        this.show()
      }
    }

    show() {
      if (this._isForcedClosed() || this._isOpen()) {
        return
      }

      this.$toggles.attr('aria-expanded', true)
      this.$element.attr('aria-expanded', true)
      this.$element.attr('aria-hidden', false)

      // focus on the first focusable item
      let $focusOn = this.$element.find(this.config.focusSelector)
      if ($focusOn.length > 0) {
        $focusOn.first().focus()
      }

      this.$container.addClass(ClassName.DRAWER_IN)
      // backdrop is responsively styled based on bmd-drawer-overlay, therefore style is none of our concern, simply add the marker class and let the scss determine if it should be displayed or not.
      this.$backdrop.addClass(ClassName.IN)
    }

    hide() {
      if (!this._isOpen()) {
        return
      }

      this.$toggles.attr('aria-expanded', false)
      this.$element.attr('aria-expanded', false)
      this.$element.attr('aria-hidden', true)

      this.$container.removeClass(ClassName.DRAWER_IN)
      this.$backdrop.removeClass(ClassName.IN)
    }


    // ------------------------------------------------------------------------
    // private

    _isOpen() {
      return this.$container.hasClass(ClassName.DRAWER_IN)
    }

    _isForcedClosed() {
      return this.$container.hasClass(ClassName.DRAWER_OUT)
    }

    _addAria() {
      let isOpen = this._isOpen()
      this.$element.attr('aria-expanded', isOpen)
      this.$element.attr('aria-hidden', isOpen)

      if (this.$toggles.length) {
        this.$toggles.attr('aria-expanded', isOpen)
      }
    }

    // ------------------------------------------------------------------------
    // static
    static _jQueryInterface(config) {
      return this.each(function () {
        let $element = $(this)
        let data = $element.data(DATA_KEY)

        if (!data) {
          data = new Drawer($element, config)
          $element.data(DATA_KEY, data)
        }
      })
    }
  }

  /**
   * ------------------------------------------------------------------------
   * jQuery
   * ------------------------------------------------------------------------
   */
  $.fn[JQUERY_NAME] = Drawer._jQueryInterface
  $.fn[JQUERY_NAME].Constructor = Drawer
  $.fn[JQUERY_NAME].noConflict = () => {
    $.fn[JQUERY_NAME] = JQUERY_NO_CONFLICT
    return Drawer._jQueryInterface
  }

  return Drawer

})(jQuery)

export default Drawer