UNPKG

9.54 kBJavaScriptView Raw
1(function ($) {
2 var materialChipsDefaults = {
3 data: [],
4 placeholder: '',
5 secondaryPlaceholder: '',
6 autocompleteData: {},
7 autocompleteLimit: Infinity,
8 };
9
10 $(document).ready(function() {
11 // Handle removal of static chips.
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 // Initialize
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 // delete
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 // left
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 // right
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 // Remove active if empty
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 // enter
165 if (13 === e.which) {
166 // Override enter if autocompleting.
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 // delete or left
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 // Click on delete icon in chip.
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 // Set for attribute for label
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 // Setup autocomplete if needed.
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 // init
314 this.init();
315
316 this.handleEvents();
317 };
318}( jQuery ));