UNPKG

fuelux

Version:

Base Fuel UX styles and controls

392 lines (315 loc) 9.87 kB
/* global jQuery:true */ /* * Fuel UX Combobox * https://github.com/ExactTarget/fuelux * * Copyright (c) 2014 ExactTarget * Licensed under the BSD New license. */ // -- BEGIN UMD WRAPPER PREFACE -- // For more information on UMD visit: // https://github.com/umdjs/umd/blob/master/jqueryPlugin.js (function umdFactory (factory) { if (typeof define === 'function' && define.amd) { // if AMD loader is available, register as an anonymous module. define(['jquery'], factory); } else if (typeof exports === 'object') { // Node/CommonJS module.exports = factory(require('jquery')); } else { // OR use browser globals if AMD is not present factory(jQuery); } }(function ComboboxWrapper ($) { // -- END UMD WRAPPER PREFACE -- // -- BEGIN MODULE CODE HERE -- var old = $.fn.combobox; // COMBOBOX CONSTRUCTOR AND PROTOTYPE var Combobox = function (element, options) { this.$element = $(element); this.options = $.extend({}, $.fn.combobox.defaults, options); this.$dropMenu = this.$element.find('.dropdown-menu'); this.$input = this.$element.find('input'); this.$button = this.$element.find('.btn'); this.$inputGroupBtn = this.$element.find('.input-group-btn'); this.$element.on('click.fu.combobox', 'a', $.proxy(this.itemclicked, this)); this.$element.on('change.fu.combobox', 'input', $.proxy(this.inputchanged, this)); this.$element.on('shown.bs.dropdown', $.proxy(this.menuShown, this)); this.$input.on('keyup.fu.combobox', $.proxy(this.keypress, this)); // set default selection this.setDefaultSelection(); // if dropdown is empty, disable it var items = this.$dropMenu.children('li'); if( items.length === 0) { this.$button.addClass('disabled'); } // filter on load in case the first thing they do is press navigational key to pop open the menu if (this.options.filterOnKeypress) { this.options.filter(this.$dropMenu.find('li'), this.$input.val(), this); } }; Combobox.prototype = { constructor: Combobox, destroy: function () { this.$element.remove(); // remove any external bindings // [none] // set input value attrbute in markup this.$element.find('input').each(function () { $(this).attr('value', $(this).val()); }); // empty elements to return to original markup // [none] return this.$element[0].outerHTML; }, doSelect: function ($item) { if (typeof $item[0] !== 'undefined') { // remove selection from old item, may result in remove and // re-addition of class if item is the same this.$element.find('li.selected:first').removeClass('selected'); // add selection to new item this.$selectedItem = $item; this.$selectedItem.addClass('selected'); // update input this.$input.val(this.$selectedItem.text().trim()); } else { // this is a custom input, not in the menu this.$selectedItem = null; this.$element.find('li.selected:first').removeClass('selected'); } }, clearSelection: function () { this.$selectedItem = null; this.$input.val(''); this.$dropMenu.find('li').removeClass('selected'); }, menuShown: function () { if (this.options.autoResizeMenu) { this.resizeMenu(); } }, resizeMenu: function () { var width = this.$element.outerWidth(); this.$dropMenu.outerWidth(width); }, selectedItem: function () { var item = this.$selectedItem; var data = {}; if (item) { var txt = this.$selectedItem.text().trim(); data = $.extend({ text: txt }, this.$selectedItem.data()); } else { data = { text: this.$input.val().trim(), notFound: true }; } return data; }, selectByText: function (text) { var $item = $([]); this.$element.find('li').each(function () { if ((this.textContent || this.innerText || $(this).text() || '').trim().toLowerCase() === (text || '').trim().toLowerCase()) { $item = $(this); return false; } }); this.doSelect($item); }, selectByValue: function (value) { var selector = 'li[data-value="' + value + '"]'; this.selectBySelector(selector); }, selectByIndex: function (index) { // zero-based index var selector = 'li:eq(' + index + ')'; this.selectBySelector(selector); }, selectBySelector: function (selector) { var $item = this.$element.find(selector); this.doSelect($item); }, setDefaultSelection: function () { var selector = 'li[data-selected=true]:first'; var item = this.$element.find(selector); if (item.length > 0) { // select by data-attribute this.selectBySelector(selector); item.removeData('selected'); item.removeAttr('data-selected'); } }, enable: function () { this.$element.removeClass('disabled'); this.$input.removeAttr('disabled'); this.$button.removeClass('disabled'); }, disable: function () { this.$element.addClass('disabled'); this.$input.attr('disabled', true); this.$button.addClass('disabled'); }, itemclicked: function (e) { this.$selectedItem = $(e.target).parent(); // set input text and trigger input change event marked as synthetic this.$input.val(this.$selectedItem.text().trim()).trigger('change', { synthetic: true }); // pass object including text and any data-attributes // to onchange event var data = this.selectedItem(); // trigger changed event this.$element.trigger('changed.fu.combobox', data); e.preventDefault(); // return focus to control after selecting an option this.$element.find('.dropdown-toggle').focus(); }, keypress: function (e) { var ENTER = 13; //var TAB = 9; var ESC = 27; var LEFT = 37; var UP = 38; var RIGHT = 39; var DOWN = 40; var IS_NAVIGATIONAL = ( e.which === UP || e.which === DOWN || e.which === LEFT || e.which === RIGHT ); if(this.options.showOptionsOnKeypress && !this.$inputGroupBtn.hasClass('open')){ this.$button.dropdown('toggle'); this.$input.focus(); } if (e.which === ENTER) { e.preventDefault(); var selected = this.$dropMenu.find('li.selected').text().trim(); if(selected.length > 0){ this.selectByText(selected); }else{ this.selectByText(this.$input.val()); } this.$inputGroupBtn.removeClass('open'); } else if (e.which === ESC) { e.preventDefault(); this.clearSelection(); this.$inputGroupBtn.removeClass('open'); } else if (this.options.showOptionsOnKeypress) { if (e.which === DOWN || e.which === UP) { e.preventDefault(); var $selected = this.$dropMenu.find('li.selected'); if ($selected.length > 0) { if (e.which === DOWN) { $selected = $selected.next(':not(.hidden)'); } else { $selected = $selected.prev(':not(.hidden)'); } } if ($selected.length === 0){ if (e.which === DOWN) { $selected = this.$dropMenu.find('li:not(.hidden):first'); } else { $selected = this.$dropMenu.find('li:not(.hidden):last'); } } this.doSelect($selected); } } // Avoid filtering on navigation key presses if (this.options.filterOnKeypress && !IS_NAVIGATIONAL) { this.options.filter(this.$dropMenu.find('li'), this.$input.val(), this); } this.previousKeyPress = e.which; }, inputchanged: function (e, extra) { var val = $(e.target).val(); // skip processing for internally-generated synthetic event // to avoid double processing if (extra && extra.synthetic) { this.selectByText(val); return; } this.selectByText(val); // find match based on input // if no match, pass the input value var data = this.selectedItem(); if (data.text.length === 0) { data = { text: val }; } // trigger changed event this.$element.trigger('changed.fu.combobox', data); } }; Combobox.prototype.getValue = Combobox.prototype.selectedItem; // COMBOBOX PLUGIN DEFINITION $.fn.combobox = function (option) { var args = Array.prototype.slice.call(arguments, 1); var methodReturn; var $set = this.each(function () { var $this = $(this); var data = $this.data('fu.combobox'); var options = typeof option === 'object' && option; if (!data) { $this.data('fu.combobox', (data = new Combobox(this, options))); } if (typeof option === 'string') { methodReturn = data[option].apply(data, args); } }); return (methodReturn === undefined) ? $set : methodReturn; }; $.fn.combobox.defaults = { autoResizeMenu: true, filterOnKeypress: false, showOptionsOnKeypress: false, filter: function filter (list, predicate, self) { var visible = 0; self.$dropMenu.find('.empty-indicator').remove(); list.each(function (i) { var $li = $(this); var text = $(this).text().trim(); $li.removeClass(); if (text === predicate) { $li.addClass('text-success'); visible++; } else if (text.substr(0, predicate.length) === predicate) { $li.addClass('text-info'); visible++; } else { $li.addClass('hidden'); } }); if (visible === 0) { self.$dropMenu.append('<li class="empty-indicator text-muted"><em>No Matches</em></li>'); } } }; $.fn.combobox.Constructor = Combobox; $.fn.combobox.noConflict = function () { $.fn.combobox = old; return this; }; // DATA-API $(document).on('mousedown.fu.combobox.data-api', '[data-initialize=combobox]', function (e) { var $control = $(e.target).closest('.combobox'); if (!$control.data('fu.combobox')) { $control.combobox($control.data()); } }); // Must be domReady for AMD compatibility $(function () { $('[data-initialize=combobox]').each(function () { var $this = $(this); if (!$this.data('fu.combobox')) { $this.combobox($this.data()); } }); }); // -- BEGIN UMD WRAPPER AFTERWORD -- })); // -- END UMD WRAPPER AFTERWORD --