1 | (function ($) {
|
2 | var materialChipsDefaults = {
|
3 | data: [],
|
4 | placeholder: '',
|
5 | secondaryPlaceholder: '',
|
6 | autocompleteData: {},
|
7 | autocompleteLimit: Infinity,
|
8 | };
|
9 |
|
10 | $(document).ready(function() {
|
11 |
|
12 | $(document).on('click', '.chip .close', function(e){
|
13 | var $chips = $(this).closest('.chips');
|
14 | if ($chips.attr('data-initialized')) {
|
15 | return;
|
16 | }
|
17 | $(this).closest('.chip').remove();
|
18 | });
|
19 | });
|
20 |
|
21 | $.fn.material_chip = function (options) {
|
22 | var self = this;
|
23 | this.$el = $(this);
|
24 | this.$document = $(document);
|
25 | this.SELS = {
|
26 | CHIPS: '.chips',
|
27 | CHIP: '.chip',
|
28 | INPUT: 'input',
|
29 | DELETE: '.material-icons',
|
30 | SELECTED_CHIP: '.selected',
|
31 | };
|
32 |
|
33 | if ('data' === options) {
|
34 | return this.$el.data('chips');
|
35 | }
|
36 |
|
37 | var curr_options = $.extend({}, materialChipsDefaults, options);
|
38 | self.hasAutocomplete = !$.isEmptyObject(curr_options.autocompleteData);
|
39 |
|
40 |
|
41 | this.init = function() {
|
42 | var i = 0;
|
43 | var chips;
|
44 | self.$el.each(function(){
|
45 | var $chips = $(this);
|
46 | var chipId = Materialize.guid();
|
47 | self.chipId = chipId;
|
48 |
|
49 | if (!curr_options.data || !(curr_options.data instanceof Array)) {
|
50 | curr_options.data = [];
|
51 | }
|
52 | $chips.data('chips', curr_options.data);
|
53 | $chips.attr('data-index', i);
|
54 | $chips.attr('data-initialized', true);
|
55 |
|
56 | if (!$chips.hasClass(self.SELS.CHIPS)) {
|
57 | $chips.addClass('chips');
|
58 | }
|
59 |
|
60 | self.chips($chips, chipId);
|
61 | i++;
|
62 | });
|
63 | };
|
64 |
|
65 | this.handleEvents = function() {
|
66 | var SELS = self.SELS;
|
67 |
|
68 | self.$document.off('click.chips-focus', SELS.CHIPS).on('click.chips-focus', SELS.CHIPS, function(e){
|
69 | $(e.target).find(SELS.INPUT).focus();
|
70 | });
|
71 |
|
72 | self.$document.off('click.chips-select', SELS.CHIP).on('click.chips-select', SELS.CHIP, function(e){
|
73 | var $chip = $(e.target);
|
74 | if ($chip.length) {
|
75 | var wasSelected = $chip.hasClass('selected');
|
76 | var $chips = $chip.closest(SELS.CHIPS);
|
77 | $(SELS.CHIP).removeClass('selected');
|
78 |
|
79 | if (!wasSelected) {
|
80 | self.selectChip($chip.index(), $chips);
|
81 | }
|
82 | }
|
83 | });
|
84 |
|
85 | self.$document.off('keydown.chips').on('keydown.chips', function(e){
|
86 | if ($(e.target).is('input, textarea')) {
|
87 | return;
|
88 | }
|
89 |
|
90 |
|
91 | var $chip = self.$document.find(SELS.CHIP + SELS.SELECTED_CHIP);
|
92 | var $chips = $chip.closest(SELS.CHIPS);
|
93 | var length = $chip.siblings(SELS.CHIP).length;
|
94 | var index;
|
95 |
|
96 | if (!$chip.length) {
|
97 | return;
|
98 | }
|
99 |
|
100 | if (e.which === 8 || e.which === 46) {
|
101 | e.preventDefault();
|
102 |
|
103 | index = $chip.index();
|
104 | self.deleteChip(index, $chips);
|
105 |
|
106 | var selectIndex = null;
|
107 | if ((index + 1) < length) {
|
108 | selectIndex = index;
|
109 | } else if (index === length || (index + 1) === length) {
|
110 | selectIndex = length - 1;
|
111 | }
|
112 |
|
113 | if (selectIndex < 0) selectIndex = null;
|
114 |
|
115 | if (null !== selectIndex) {
|
116 | self.selectChip(selectIndex, $chips);
|
117 | }
|
118 | if (!length) $chips.find('input').focus();
|
119 |
|
120 |
|
121 | } else if (e.which === 37) {
|
122 | index = $chip.index() - 1;
|
123 | if (index < 0) {
|
124 | return;
|
125 | }
|
126 | $(SELS.CHIP).removeClass('selected');
|
127 | self.selectChip(index, $chips);
|
128 |
|
129 |
|
130 | } else if (e.which === 39) {
|
131 | index = $chip.index() + 1;
|
132 | $(SELS.CHIP).removeClass('selected');
|
133 | if (index > length) {
|
134 | $chips.find('input').focus();
|
135 | return;
|
136 | }
|
137 | self.selectChip(index, $chips);
|
138 | }
|
139 | });
|
140 |
|
141 | self.$document.off('focusin.chips', SELS.CHIPS + ' ' + SELS.INPUT).on('focusin.chips', SELS.CHIPS + ' ' + SELS.INPUT, function(e){
|
142 | var $currChips = $(e.target).closest(SELS.CHIPS);
|
143 | $currChips.addClass('focus');
|
144 | $currChips.siblings('label, .prefix').addClass('active');
|
145 | $(SELS.CHIP).removeClass('selected');
|
146 | });
|
147 |
|
148 | self.$document.off('focusout.chips', SELS.CHIPS + ' ' + SELS.INPUT).on('focusout.chips', SELS.CHIPS + ' ' + SELS.INPUT, function(e){
|
149 | var $currChips = $(e.target).closest(SELS.CHIPS);
|
150 | $currChips.removeClass('focus');
|
151 |
|
152 |
|
153 | if (!$currChips.data('chips').length) {
|
154 | $currChips.siblings('label').removeClass('active');
|
155 | }
|
156 | $currChips.siblings('.prefix').removeClass('active');
|
157 | });
|
158 |
|
159 | self.$document.off('keydown.chips-add', SELS.CHIPS + ' ' + SELS.INPUT).on('keydown.chips-add', SELS.CHIPS + ' ' + SELS.INPUT, function(e){
|
160 | var $target = $(e.target);
|
161 | var $chips = $target.closest(SELS.CHIPS);
|
162 | var chipsLength = $chips.children(SELS.CHIP).length;
|
163 |
|
164 |
|
165 | if (13 === e.which) {
|
166 |
|
167 | if (self.hasAutocomplete &&
|
168 | $chips.find('.autocomplete-content.dropdown-content').length &&
|
169 | $chips.find('.autocomplete-content.dropdown-content').children().length) {
|
170 | return;
|
171 | }
|
172 |
|
173 | e.preventDefault();
|
174 | self.addChip({tag: $target.val()}, $chips);
|
175 | $target.val('');
|
176 | return;
|
177 | }
|
178 |
|
179 |
|
180 | if ((8 === e.keyCode || 37 === e.keyCode) && '' === $target.val() && chipsLength) {
|
181 | e.preventDefault();
|
182 | self.selectChip(chipsLength - 1, $chips);
|
183 | $target.blur();
|
184 | return;
|
185 | }
|
186 | });
|
187 |
|
188 |
|
189 | self.$document.off('click.chips-delete', SELS.CHIPS + ' ' + SELS.DELETE).on('click.chips-delete', SELS.CHIPS + ' ' + SELS.DELETE, function(e) {
|
190 | var $target = $(e.target);
|
191 | var $chips = $target.closest(SELS.CHIPS);
|
192 | var $chip = $target.closest(SELS.CHIP);
|
193 | e.stopPropagation();
|
194 | self.deleteChip($chip.index(), $chips);
|
195 | $chips.find('input').focus();
|
196 | });
|
197 | };
|
198 |
|
199 | this.chips = function($chips, chipId) {
|
200 | var html = '';
|
201 | $chips.data('chips').forEach(function(elem){
|
202 | html += self.renderChip(elem);
|
203 | });
|
204 | html += '<input id="' + chipId +'" class="input" placeholder="">';
|
205 | $chips.html(html);
|
206 | self.setPlaceholder($chips);
|
207 |
|
208 |
|
209 | var label = $chips.next('label');
|
210 | if (label.length) {
|
211 | label.attr('for', chipId);
|
212 |
|
213 | if ($chips.data('chips').length) {
|
214 | label.addClass('active');
|
215 | }
|
216 | }
|
217 |
|
218 |
|
219 | var input = $('#' + chipId);
|
220 | if (self.hasAutocomplete) {
|
221 | input.autocomplete({
|
222 | data: curr_options.autocompleteData,
|
223 | limit: curr_options.autocompleteLimit,
|
224 | onAutocomplete: function(val) {
|
225 | self.addChip({tag: val}, $chips);
|
226 | input.val('');
|
227 | input.focus();
|
228 | },
|
229 | })
|
230 | }
|
231 | };
|
232 |
|
233 | this.renderChip = function(elem) {
|
234 | if (!elem.tag) return;
|
235 |
|
236 | var html = '<div class="chip">' + elem.tag;
|
237 | if (elem.image) {
|
238 | html += ' <img src="' + elem.image + '"> ';
|
239 | }
|
240 | html += '<i class="material-icons close">close</i>';
|
241 | html += '</div>';
|
242 | return html;
|
243 | };
|
244 |
|
245 | this.setPlaceholder = function($chips) {
|
246 | if ($chips.data('chips').length && curr_options.placeholder) {
|
247 | $chips.find('input').prop('placeholder', curr_options.placeholder);
|
248 |
|
249 | } else if (!$chips.data('chips').length && curr_options.secondaryPlaceholder) {
|
250 | $chips.find('input').prop('placeholder', curr_options.secondaryPlaceholder);
|
251 | }
|
252 | };
|
253 |
|
254 | this.isValid = function($chips, elem) {
|
255 | var chips = $chips.data('chips');
|
256 | var exists = false;
|
257 | for (var i=0; i < chips.length; i++) {
|
258 | if (chips[i].tag === elem.tag) {
|
259 | exists = true;
|
260 | return;
|
261 | }
|
262 | }
|
263 | return '' !== elem.tag && !exists;
|
264 | };
|
265 |
|
266 | this.addChip = function(elem, $chips) {
|
267 | if (!self.isValid($chips, elem)) {
|
268 | return;
|
269 | }
|
270 | var chipHtml = self.renderChip(elem);
|
271 | var newData = [];
|
272 | var oldData = $chips.data('chips');
|
273 | for (var i = 0; i < oldData.length; i++) {
|
274 | newData.push(oldData[i]);
|
275 | }
|
276 | newData.push(elem);
|
277 |
|
278 | $chips.data('chips', newData);
|
279 | $(chipHtml).insertBefore($chips.find('input'));
|
280 | $chips.trigger('chip.add', elem);
|
281 | self.setPlaceholder($chips);
|
282 | };
|
283 |
|
284 | this.deleteChip = function(chipIndex, $chips) {
|
285 | var chip = $chips.data('chips')[chipIndex];
|
286 | $chips.find('.chip').eq(chipIndex).remove();
|
287 |
|
288 | var newData = [];
|
289 | var oldData = $chips.data('chips');
|
290 | for (var i = 0; i < oldData.length; i++) {
|
291 | if (i !== chipIndex) {
|
292 | newData.push(oldData[i]);
|
293 | }
|
294 | }
|
295 |
|
296 | $chips.data('chips', newData);
|
297 | $chips.trigger('chip.delete', chip);
|
298 | self.setPlaceholder($chips);
|
299 | };
|
300 |
|
301 | this.selectChip = function(chipIndex, $chips) {
|
302 | var $chip = $chips.find('.chip').eq(chipIndex);
|
303 | if ($chip && false === $chip.hasClass('selected')) {
|
304 | $chip.addClass('selected');
|
305 | $chips.trigger('chip.select', $chips.data('chips')[chipIndex]);
|
306 | }
|
307 | };
|
308 |
|
309 | this.getChipsElement = function(index, $chips) {
|
310 | return $chips.eq(index);
|
311 | };
|
312 |
|
313 |
|
314 | this.init();
|
315 |
|
316 | this.handleEvents();
|
317 | };
|
318 | }( jQuery ));
|