summaryrefslogtreecommitdiff
path: root/templates/assets/bootstrap-material-design/js/bootstrapMaterialDesign.js
blob: b3be5316162f67722be58a7add84d5d481f5aa3b (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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
/**
 * $.bootstrapMaterialDesign(config) is a macro class to configure the components generally
 *  used in Bootstrap Material Design.  You may pass overrides to the configurations
 *  which will be passed into each component, or you may omit use of this class and
 *  configure each component separately.
 */
const BootstrapMaterialDesign = (($) => {

  /**
   * ------------------------------------------------------------------------
   * Constants
   * ------------------------------------------------------------------------
   */
  const NAME = 'bootstrapMaterialDesign'
  const DATA_KEY = `bmd.${NAME}`
  const JQUERY_NAME = NAME // retain this full name since it is long enough not to conflict
  const JQUERY_NO_CONFLICT = $.fn[JQUERY_NAME]

  /**
   * Global configuration:
   *  The global configuration hash will be mixed in to each components' config.
   *    e.g. calling $.bootstrapMaterialDesign({global: { validate: true } }) would pass `validate:true` to every component
   *
   *
   * Component configuration:
   *  - selector: may be a string or an array.  Any array will be joined with a comma to generate the selector
   *  - disable any component by defining it as false with an override. e.g. $.bootstrapMaterialDesign({ autofill: false })
   *
   *  @see each individual component for more configuration settings.
   */
  const Default = {
    global: {
      validate: false,
      label: {
        className: 'bmd-label-static' // default style of label to be used if not specified in the html markup
      }
    },
    autofill: {
      selector: 'body'
    },
    checkbox: {
      selector: '.checkbox > label > input[type=checkbox]'
    },
    checkboxInline: {
      selector: 'label.checkbox-inline > input[type=checkbox]'
    },
    collapseInline: {
      selector: '.bmd-collapse-inline [data-toggle="collapse"]'
    },
    drawer: {
      selector: '.bmd-layout-drawer'
    },
    file: {
      selector: 'input[type=file]'
    },
    radio: {
      selector: '.radio > label > input[type=radio]'
    },
    radioInline: {
      selector: 'label.radio-inline > input[type=radio]'
    },
    ripples: {
      //selector: ['.btn:not(.btn-link):not(.ripple-none)'] // testing only
      selector: [
        '.btn:not(.btn-link):not(.ripple-none)',
        '.card-image:not(.ripple-none)',
        '.navbar a:not(.ripple-none)',
        '.dropdown-menu a:not(.ripple-none)',
        '.nav-tabs a:not(.ripple-none)',
        '.pagination li:not(.active):not(.disabled) a:not(.ripple-none)',
        '.ripple' // generic marker class to add ripple to elements
      ]
    },
    select: {
      selector: ['select']
    },
    switch: {
      selector: '.switch > label > input[type=checkbox]'
    },
    text: {
      // omit inputs we have specialized components to handle - we need to match text, email, etc.  The easiest way to do this appears to be just omit the ones we don't want to match and let the rest fall through to this.
      selector: [`input:not([type=hidden]):not([type=checkbox]):not([type=radio]):not([type=file]):not([type=button]):not([type=submit]):not([type=reset])`]
    },
    textarea: {
      selector: ['textarea']
    },
    arrive: true,
    // create an ordered component list for instantiation
    instantiation: [
      'ripples',
      'checkbox',
      'checkboxInline',
      'collapseInline',
      'drawer',
      //'file',
      'radio',
      'radioInline',
      'switch',
      'text',
      'textarea',
      'select',
      'autofill'
    ]
  }

  /**
   * ------------------------------------------------------------------------
   * Class Definition
   * ------------------------------------------------------------------------
   */
  class BootstrapMaterialDesign {

    constructor($element, config) {
      this.$element = $element
      this.config = $.extend(true, {}, Default, config)
      let $document = $(document)

      for (let component of this.config.instantiation) {

        // the component's config fragment is passed in directly, allowing users to override
        let componentConfig = this.config[component]

        // check to make sure component config is enabled (not `false`)
        if (componentConfig) {

          // assemble the selector as it may be an array
          let selector = this._resolveSelector(componentConfig)

          // mix in global options
          componentConfig = $.extend(true, {}, this.config.global, componentConfig)

          // create the jquery fn name e.g. 'bmdText' for 'text'
          let componentName = `${component.charAt(0).toUpperCase() + component.slice(1)}`
          let jqueryFn = `bmd${componentName}`

          try {
            // safely instantiate component on selector elements with config, report errors and move on.
            // console.debug(`instantiating: $('${selector}')[${jqueryFn}](${componentConfig})`) // eslint-disable-line no-console
            $(selector)[jqueryFn](componentConfig)

            // add to arrive if present and enabled
            if (document.arrive && this.config.arrive) {
              $document.arrive(selector, function(){  // eslint-disable-line no-loop-func
                $(this)[jqueryFn](componentConfig)
              })
            }
          } catch (e) {
            let message = `Failed to instantiate component: $('${selector}')[${jqueryFn}](${componentConfig})`
            console.error(message, e, `\nSelected elements: `, $(selector)) // eslint-disable-line no-console
            throw e
          }
        }
      }
    }

    dispose() {
      this.$element.data(DATA_KEY, null)
      this.$element = null
      this.config = null
    }

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

    _resolveSelector(componentConfig) {
      let selector = componentConfig.selector
      if (Array.isArray(selector)) {
        selector = selector.join(', ')
      }

      return selector
    }

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

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

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

  return BootstrapMaterialDesign

})(jQuery)

export default BootstrapMaterialDesign