@kamranbaylarov/one-select 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +848 -0
- package/css/one-select.min.css +37 -0
- package/js/one-select.js +1227 -0
- package/js/one-select.min.js +6 -0
- package/package.json +41 -0
package/js/one-select.js
ADDED
|
@@ -0,0 +1,1227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OneSelect - jQuery Multi-Select Dropdown Plugin
|
|
3
|
+
* Version: 1.0.0
|
|
4
|
+
* https://github.com/your-repo/one-select
|
|
5
|
+
*
|
|
6
|
+
* Copyright 2024
|
|
7
|
+
* Licensed under MIT
|
|
8
|
+
*
|
|
9
|
+
* A powerful, flexible, and feature-rich multi-select dropdown component for jQuery.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
(function(factory) {
|
|
13
|
+
'use strict';
|
|
14
|
+
|
|
15
|
+
if (typeof define === 'function' && define.amd) {
|
|
16
|
+
// AMD
|
|
17
|
+
define(['jquery'], factory);
|
|
18
|
+
} else if (typeof module === 'object' && module.exports) {
|
|
19
|
+
// CommonJS
|
|
20
|
+
module.exports = function(root, jQuery) {
|
|
21
|
+
if (jQuery === undefined) {
|
|
22
|
+
if (typeof window !== 'undefined') {
|
|
23
|
+
jQuery = require('jquery');
|
|
24
|
+
} else {
|
|
25
|
+
jQuery = require('jquery')(root);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
factory(jQuery);
|
|
29
|
+
return jQuery;
|
|
30
|
+
};
|
|
31
|
+
} else {
|
|
32
|
+
// Browser globals
|
|
33
|
+
factory(window.jQuery || window.$);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
}(function($) {
|
|
37
|
+
'use strict';
|
|
38
|
+
|
|
39
|
+
// Global registry for all instances
|
|
40
|
+
var instances = {};
|
|
41
|
+
var pluginName = 'oneSelect';
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Debounce utility function
|
|
45
|
+
* @param {Function} func - Function to debounce
|
|
46
|
+
* @param {Number} delay - Delay in milliseconds
|
|
47
|
+
* @returns {Function} Debounced function
|
|
48
|
+
*/
|
|
49
|
+
function debounce(func, delay) {
|
|
50
|
+
var timeoutId;
|
|
51
|
+
return function() {
|
|
52
|
+
var context = this;
|
|
53
|
+
var args = arguments;
|
|
54
|
+
clearTimeout(timeoutId);
|
|
55
|
+
timeoutId = setTimeout(function() {
|
|
56
|
+
func.apply(context, args);
|
|
57
|
+
}, delay);
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* OneSelect Constructor
|
|
63
|
+
* @param {HTMLElement} element - The DOM element
|
|
64
|
+
* @param {Object} options - Configuration options
|
|
65
|
+
*/
|
|
66
|
+
var OneSelect = function(element, options) {
|
|
67
|
+
this.element = element;
|
|
68
|
+
this.$element = $(element);
|
|
69
|
+
|
|
70
|
+
// Generate unique instance ID for control
|
|
71
|
+
this.instanceId = 'ones-' + Math.random().toString(36).substr(2, 9);
|
|
72
|
+
|
|
73
|
+
// Read data attributes from element (highest priority)
|
|
74
|
+
var dataOptions = this.readDataAttributes();
|
|
75
|
+
|
|
76
|
+
// Merge: defaults -> options (JS) -> dataOptions (HTML attributes)
|
|
77
|
+
this.settings = $.extend({}, OneSelect.defaults, options, dataOptions);
|
|
78
|
+
this.init();
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Default configuration options
|
|
83
|
+
*/
|
|
84
|
+
OneSelect.defaults = {
|
|
85
|
+
placeholder: 'Select options...',
|
|
86
|
+
selectAllText: 'Select All',
|
|
87
|
+
okText: 'OK',
|
|
88
|
+
cancelText: 'Cancel',
|
|
89
|
+
data: [],
|
|
90
|
+
selectedValues: [],
|
|
91
|
+
value: null, // Single value or array of values to pre-select
|
|
92
|
+
valueField: 'value',
|
|
93
|
+
labelField: 'label',
|
|
94
|
+
showCheckbox: true,
|
|
95
|
+
showBadges: false,
|
|
96
|
+
showBadgesExternal: null,
|
|
97
|
+
showSearch: false, // Show search input in dropdown
|
|
98
|
+
searchPlaceholder: 'Search...',
|
|
99
|
+
searchUrl: null, // URL for AJAX search (GET request)
|
|
100
|
+
searchDebounceDelay: 300,// Delay in milliseconds for search debounce
|
|
101
|
+
closeOnScroll: false,
|
|
102
|
+
closeOnOutside: true, // Close dropdown when clicking outside (default: true)
|
|
103
|
+
submitForm: false,
|
|
104
|
+
submitOnOutside: false,
|
|
105
|
+
formId: null,
|
|
106
|
+
name: null,
|
|
107
|
+
multiple: true,
|
|
108
|
+
ajax: null,
|
|
109
|
+
autoLoad: true,
|
|
110
|
+
beforeLoad: null,
|
|
111
|
+
afterLoad: null,
|
|
112
|
+
onLoadError: null,
|
|
113
|
+
onChange: null,
|
|
114
|
+
onSelect: null,
|
|
115
|
+
onOk: null,
|
|
116
|
+
onCancel: null
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* OneSelect Prototype
|
|
121
|
+
*/
|
|
122
|
+
OneSelect.prototype = {
|
|
123
|
+
/**
|
|
124
|
+
* Read data attributes from HTML element
|
|
125
|
+
*/
|
|
126
|
+
readDataAttributes: function() {
|
|
127
|
+
var self = this;
|
|
128
|
+
var dataOptions = {};
|
|
129
|
+
|
|
130
|
+
var attributeMap = {
|
|
131
|
+
'ones-placeholder': 'placeholder',
|
|
132
|
+
'ones-select-all-text': 'selectAllText',
|
|
133
|
+
'ones-ok-text': 'okText',
|
|
134
|
+
'ones-cancel-text': 'cancelText',
|
|
135
|
+
'ones-data': 'data',
|
|
136
|
+
'ones-selected': 'selectedValues',
|
|
137
|
+
'ones-value': 'value',
|
|
138
|
+
'ones-value-field': 'valueField',
|
|
139
|
+
'ones-label-field': 'labelField',
|
|
140
|
+
'ones-name': 'name',
|
|
141
|
+
'ones-multiple': 'multiple',
|
|
142
|
+
'ones-show-checkbox': 'showCheckbox',
|
|
143
|
+
'ones-show-badges': 'showBadges',
|
|
144
|
+
'ones-show-badges-external': 'showBadgesExternal',
|
|
145
|
+
'ones-show-search': 'showSearch',
|
|
146
|
+
'ones-search-placeholder': 'searchPlaceholder',
|
|
147
|
+
'ones-search-url': 'searchUrl',
|
|
148
|
+
'ones-search-debounce-delay': 'searchDebounceDelay',
|
|
149
|
+
'ones-close-on-scroll': 'closeOnScroll',
|
|
150
|
+
'ones-close-on-outside': 'closeOnOutside',
|
|
151
|
+
'ones-submit-form': 'submitForm',
|
|
152
|
+
'ones-submit-on-outside': 'submitOnOutside',
|
|
153
|
+
'ones-form-id': 'formId',
|
|
154
|
+
'ones-auto-load': 'autoLoad'
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
$.each(attributeMap, function(attr, setting) {
|
|
158
|
+
var value = self.$element.data(attr);
|
|
159
|
+
|
|
160
|
+
if (value === undefined) {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (setting === 'data' || setting === 'selectedValues' || setting === 'value') {
|
|
165
|
+
if (typeof value === 'string') {
|
|
166
|
+
try {
|
|
167
|
+
dataOptions[setting] = JSON.parse(value);
|
|
168
|
+
} catch (e) {
|
|
169
|
+
console.warn('OneSelect: Invalid JSON for ' + attr, value);
|
|
170
|
+
dataOptions[setting] = value;
|
|
171
|
+
}
|
|
172
|
+
} else {
|
|
173
|
+
dataOptions[setting] = value;
|
|
174
|
+
}
|
|
175
|
+
} else if (setting === 'multiple' || setting === 'showCheckbox' ||
|
|
176
|
+
setting === 'showBadges' || setting === 'showSearch' ||
|
|
177
|
+
setting === 'closeOnScroll' || setting === 'closeOnOutside' ||
|
|
178
|
+
setting === 'submitForm' || setting === 'submitOnOutside' ||
|
|
179
|
+
setting === 'autoLoad') {
|
|
180
|
+
if (typeof value === 'string') {
|
|
181
|
+
dataOptions[setting] = value === 'true' || value === '1';
|
|
182
|
+
} else {
|
|
183
|
+
dataOptions[setting] = !!value;
|
|
184
|
+
}
|
|
185
|
+
} else if (setting === 'searchDebounceDelay') {
|
|
186
|
+
// Parse as number
|
|
187
|
+
if (typeof value === 'string') {
|
|
188
|
+
dataOptions[setting] = parseInt(value, 10) || 300;
|
|
189
|
+
} else {
|
|
190
|
+
dataOptions[setting] = value || 300;
|
|
191
|
+
}
|
|
192
|
+
} else {
|
|
193
|
+
dataOptions[setting] = value;
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
var ajaxData = this.$element.data('ones-ajax');
|
|
198
|
+
if (ajaxData) {
|
|
199
|
+
if (typeof ajaxData === 'string') {
|
|
200
|
+
try {
|
|
201
|
+
dataOptions.ajax = JSON.parse(ajaxData);
|
|
202
|
+
} catch (e) {
|
|
203
|
+
console.warn('OneSelect: Invalid JSON for ones-ajax', ajaxData);
|
|
204
|
+
}
|
|
205
|
+
} else {
|
|
206
|
+
dataOptions.ajax = ajaxData;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return dataOptions;
|
|
211
|
+
},
|
|
212
|
+
|
|
213
|
+
init: function() {
|
|
214
|
+
// Register instance in global registry
|
|
215
|
+
instances[this.instanceId] = this;
|
|
216
|
+
|
|
217
|
+
// Merge value parameter into selectedValues
|
|
218
|
+
this.settings.selectedValues = this.mergeValueSettings(this.settings.value, this.settings.selectedValues);
|
|
219
|
+
|
|
220
|
+
this.wrapper = this.createWrapper();
|
|
221
|
+
this.trigger = this.createTrigger();
|
|
222
|
+
this.dropdown = this.createDropdown();
|
|
223
|
+
this.searchInput = this.createSearchInput();
|
|
224
|
+
this.optionsContainer = this.createOptionsContainer();
|
|
225
|
+
this.buttons = this.createButtons();
|
|
226
|
+
|
|
227
|
+
this.build();
|
|
228
|
+
this.attachEvents();
|
|
229
|
+
|
|
230
|
+
if (this.settings.ajax && this.settings.autoLoad) {
|
|
231
|
+
this.loadData();
|
|
232
|
+
}
|
|
233
|
+
},
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Merge value parameter into selectedValues array
|
|
237
|
+
* @param {*} value - Single value or array of values
|
|
238
|
+
* @param {Array} selectedValues - Existing selected values
|
|
239
|
+
* @returns {Array} Merged array of selected values
|
|
240
|
+
*/
|
|
241
|
+
mergeValueSettings: function(value, selectedValues) {
|
|
242
|
+
var result = selectedValues ? [].concat(selectedValues) : [];
|
|
243
|
+
|
|
244
|
+
if (value !== null && value !== undefined) {
|
|
245
|
+
if (Array.isArray(value)) {
|
|
246
|
+
// value is an array - merge all items
|
|
247
|
+
$.each(value, function(i, v) {
|
|
248
|
+
if ($.inArray(v, result) === -1) {
|
|
249
|
+
result.push(v);
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
} else {
|
|
253
|
+
// value is a single value - add if not already present
|
|
254
|
+
if ($.inArray(value, result) === -1) {
|
|
255
|
+
result.push(value);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return result;
|
|
261
|
+
},
|
|
262
|
+
|
|
263
|
+
build: function() {
|
|
264
|
+
// Add search input at the top of dropdown if enabled
|
|
265
|
+
if (this.settings.showSearch) {
|
|
266
|
+
this.dropdown.append(this.searchInput);
|
|
267
|
+
}
|
|
268
|
+
this.dropdown.append(this.optionsContainer);
|
|
269
|
+
this.dropdown.append(this.buttons);
|
|
270
|
+
this.wrapper.append(this.trigger);
|
|
271
|
+
|
|
272
|
+
// Append wrapper to $element, dropdown to body
|
|
273
|
+
this.$element.append(this.wrapper);
|
|
274
|
+
$('body').append(this.dropdown);
|
|
275
|
+
|
|
276
|
+
this.renderOptions();
|
|
277
|
+
this.updateTriggerText();
|
|
278
|
+
this.updateHiddenInputs();
|
|
279
|
+
},
|
|
280
|
+
|
|
281
|
+
updateHiddenInputs: function() {
|
|
282
|
+
if (!this.settings.name) {
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
var form = null;
|
|
287
|
+
if (this.settings.formId) {
|
|
288
|
+
form = $('#' + this.settings.formId);
|
|
289
|
+
} else {
|
|
290
|
+
form = this.$element.closest('form');
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
var container = form.length ? form : this.wrapper;
|
|
294
|
+
|
|
295
|
+
container.find('input.cms-hidden-input[data-cms-input="' + this.settings.name + '"]').remove();
|
|
296
|
+
|
|
297
|
+
var inputName = this.settings.name;
|
|
298
|
+
if (this.settings.multiple && inputName.indexOf('[') === -1) {
|
|
299
|
+
inputName += '[]';
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
var selectedValues = this.getSelectedValues();
|
|
303
|
+
|
|
304
|
+
if (this.settings.multiple) {
|
|
305
|
+
$.each(selectedValues, function(index, value) {
|
|
306
|
+
var hiddenInput = $('<input type="hidden" class="cms-hidden-input">')
|
|
307
|
+
.attr('name', inputName)
|
|
308
|
+
.attr('value', value)
|
|
309
|
+
.attr('data-cms-input', this.settings.name)
|
|
310
|
+
.attr('data-cms-value', value);
|
|
311
|
+
container.append(hiddenInput);
|
|
312
|
+
}.bind(this));
|
|
313
|
+
} else {
|
|
314
|
+
var value = selectedValues.length > 0 ? selectedValues.join(',') : '';
|
|
315
|
+
var hiddenInput = $('<input type="hidden" class="cms-hidden-input">')
|
|
316
|
+
.attr('name', inputName)
|
|
317
|
+
.attr('value', value)
|
|
318
|
+
.attr('data-cms-input', this.settings.name);
|
|
319
|
+
container.append(hiddenInput);
|
|
320
|
+
}
|
|
321
|
+
},
|
|
322
|
+
|
|
323
|
+
createWrapper: function() {
|
|
324
|
+
return $('<div class="cms-wrapper"></div>');
|
|
325
|
+
},
|
|
326
|
+
|
|
327
|
+
createTrigger: function() {
|
|
328
|
+
return $('<div class="cms-trigger"><span class="cms-selected-text cms-placeholder">' +
|
|
329
|
+
this.settings.placeholder + '</span></div>');
|
|
330
|
+
},
|
|
331
|
+
|
|
332
|
+
createDropdown: function() {
|
|
333
|
+
return $('<div class="cms-dropdown"></div>');
|
|
334
|
+
},
|
|
335
|
+
|
|
336
|
+
createSearchInput: function() {
|
|
337
|
+
return $('<div class="cms-search-wrapper">' +
|
|
338
|
+
'<input type="text" class="cms-search-input" placeholder="' +
|
|
339
|
+
this.settings.searchPlaceholder + '" /></div>');
|
|
340
|
+
},
|
|
341
|
+
|
|
342
|
+
createOptionsContainer: function() {
|
|
343
|
+
return $('<div class="cms-options-container"></div>');
|
|
344
|
+
},
|
|
345
|
+
|
|
346
|
+
createButtons: function() {
|
|
347
|
+
var container = $('<div class="cms-buttons"></div>');
|
|
348
|
+
this.okBtn = $('<button class="cms-btn cms-btn-ok">' + this.settings.okText + '</button>');
|
|
349
|
+
this.cancelBtn = $('<button class="cms-btn cms-btn-cancel">' + this.settings.cancelText + '</button>');
|
|
350
|
+
|
|
351
|
+
container.append(this.okBtn);
|
|
352
|
+
container.append(this.cancelBtn);
|
|
353
|
+
|
|
354
|
+
return container;
|
|
355
|
+
},
|
|
356
|
+
|
|
357
|
+
renderOptions: function() {
|
|
358
|
+
this.optionsContainer.empty();
|
|
359
|
+
|
|
360
|
+
var selectAllOption = this.createOption('select-all', this.settings.selectAllText, false);
|
|
361
|
+
this.optionsContainer.append(selectAllOption);
|
|
362
|
+
|
|
363
|
+
var self = this;
|
|
364
|
+
$.each(this.settings.data, function(index, item) {
|
|
365
|
+
var value, label;
|
|
366
|
+
|
|
367
|
+
if (typeof item === 'object') {
|
|
368
|
+
value = item[self.settings.valueField];
|
|
369
|
+
label = item[self.settings.labelField];
|
|
370
|
+
} else {
|
|
371
|
+
value = item;
|
|
372
|
+
label = item;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
var isSelected = $.inArray(value, self.settings.selectedValues) !== -1;
|
|
376
|
+
var option = self.createOption(value, label, isSelected);
|
|
377
|
+
self.optionsContainer.append(option);
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
this.updateSelectAllState();
|
|
381
|
+
},
|
|
382
|
+
|
|
383
|
+
createOption: function(value, label, checked) {
|
|
384
|
+
var optionClass = 'cms-option';
|
|
385
|
+
if (!this.settings.showCheckbox) {
|
|
386
|
+
optionClass += ' cms-hide-checkbox';
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
if (checked && value !== 'select-all') {
|
|
390
|
+
optionClass += ' selected';
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
var option = $('<div class="' + optionClass + '" data-value="' + value + '"></div>');
|
|
394
|
+
var checkbox = $('<input type="checkbox" value="' + value + '"' +
|
|
395
|
+
(checked ? ' checked' : '') + '>');
|
|
396
|
+
var labelEl = $('<label>' + label + '</label>');
|
|
397
|
+
|
|
398
|
+
option.append(checkbox);
|
|
399
|
+
option.append(labelEl);
|
|
400
|
+
|
|
401
|
+
return option;
|
|
402
|
+
},
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Filter options based on search text
|
|
406
|
+
* @param {String} searchText - Search text to filter by
|
|
407
|
+
*/
|
|
408
|
+
filterOptions: function(searchText) {
|
|
409
|
+
var self = this;
|
|
410
|
+
var options = this.optionsContainer.find('.cms-option:not([data-value="select-all"])');
|
|
411
|
+
|
|
412
|
+
if (searchText === '') {
|
|
413
|
+
// Show all options if search is empty
|
|
414
|
+
options.show();
|
|
415
|
+
} else {
|
|
416
|
+
// Filter options by label
|
|
417
|
+
options.each(function() {
|
|
418
|
+
var option = $(this);
|
|
419
|
+
var label = option.find('label').text().toLowerCase();
|
|
420
|
+
|
|
421
|
+
if (label.indexOf(searchText) !== -1) {
|
|
422
|
+
option.show();
|
|
423
|
+
} else {
|
|
424
|
+
option.hide();
|
|
425
|
+
}
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
},
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* Perform AJAX search
|
|
432
|
+
* @param {String} searchText - Search text to send to server
|
|
433
|
+
*/
|
|
434
|
+
performAjaxSearch: function(searchText) {
|
|
435
|
+
var self = this;
|
|
436
|
+
|
|
437
|
+
// Show loading state
|
|
438
|
+
this.optionsContainer.addClass('cms-loading');
|
|
439
|
+
|
|
440
|
+
$.ajax({
|
|
441
|
+
url: this.settings.searchUrl,
|
|
442
|
+
method: 'GET',
|
|
443
|
+
data: { q: searchText },
|
|
444
|
+
dataType: 'json',
|
|
445
|
+
success: function(response) {
|
|
446
|
+
// Handle different response formats
|
|
447
|
+
var data = response;
|
|
448
|
+
if (typeof response === 'object' && response.data) {
|
|
449
|
+
data = response.data;
|
|
450
|
+
} else if (typeof response === 'object' && response.results) {
|
|
451
|
+
data = response.results;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// Update dropdown with search results
|
|
455
|
+
self.updateSearchResults(data || []);
|
|
456
|
+
|
|
457
|
+
self.optionsContainer.removeClass('cms-loading');
|
|
458
|
+
},
|
|
459
|
+
error: function(xhr, status, error) {
|
|
460
|
+
console.error('OneSelect: Search error', error);
|
|
461
|
+
self.optionsContainer.removeClass('cms-loading');
|
|
462
|
+
|
|
463
|
+
if (self.settings.onLoadError) {
|
|
464
|
+
self.settings.onLoadError.call(self, xhr, status, error);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
});
|
|
468
|
+
},
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* Update dropdown with search results
|
|
472
|
+
* @param {Array} data - Search results data
|
|
473
|
+
*/
|
|
474
|
+
updateSearchResults: function(data) {
|
|
475
|
+
// Clear existing options (except select-all)
|
|
476
|
+
this.optionsContainer.find('.cms-option:not([data-value="select-all"])').remove();
|
|
477
|
+
|
|
478
|
+
var self = this;
|
|
479
|
+
$.each(data, function(index, item) {
|
|
480
|
+
var value, label;
|
|
481
|
+
|
|
482
|
+
if (typeof item === 'object') {
|
|
483
|
+
value = item[self.settings.valueField];
|
|
484
|
+
label = item[self.settings.labelField];
|
|
485
|
+
} else {
|
|
486
|
+
value = item;
|
|
487
|
+
label = item;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// Keep selection state if value was previously selected
|
|
491
|
+
var isSelected = $.inArray(value, self.settings.selectedValues) !== -1;
|
|
492
|
+
var option = self.createOption(value, label, isSelected);
|
|
493
|
+
self.optionsContainer.append(option);
|
|
494
|
+
});
|
|
495
|
+
|
|
496
|
+
// Update select-all state
|
|
497
|
+
this.updateSelectAllState();
|
|
498
|
+
},
|
|
499
|
+
|
|
500
|
+
attachEvents: function() {
|
|
501
|
+
var self = this;
|
|
502
|
+
|
|
503
|
+
this.trigger.on('click', function(e) {
|
|
504
|
+
e.stopPropagation();
|
|
505
|
+
self.toggle();
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
// Search input event listener
|
|
509
|
+
if (this.settings.showSearch) {
|
|
510
|
+
if (this.settings.searchUrl) {
|
|
511
|
+
// AJAX search with debounce
|
|
512
|
+
var debouncedSearch = debounce(function(searchText) {
|
|
513
|
+
self.performAjaxSearch(searchText);
|
|
514
|
+
}, this.settings.searchDebounceDelay);
|
|
515
|
+
|
|
516
|
+
this.searchInput.find('.cms-search-input').on('keyup', function() {
|
|
517
|
+
var searchText = $(this).val();
|
|
518
|
+
if (searchText.length > 0) {
|
|
519
|
+
debouncedSearch(searchText);
|
|
520
|
+
} else {
|
|
521
|
+
// Show original data when search is empty
|
|
522
|
+
self.filterOptions('');
|
|
523
|
+
}
|
|
524
|
+
});
|
|
525
|
+
} else {
|
|
526
|
+
// Local filtering (default)
|
|
527
|
+
this.searchInput.find('.cms-search-input').on('keyup', function() {
|
|
528
|
+
var searchText = $(this).val().toLowerCase();
|
|
529
|
+
self.filterOptions(searchText);
|
|
530
|
+
});
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
$(window).on('resize.cms', function() {
|
|
535
|
+
if (self.wrapper.hasClass('open')) {
|
|
536
|
+
self.updateDropdownPosition();
|
|
537
|
+
}
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
if (this.settings.closeOnScroll) {
|
|
541
|
+
$(window).on('scroll.cms', function() {
|
|
542
|
+
if (self.wrapper.hasClass('open')) {
|
|
543
|
+
self.close();
|
|
544
|
+
}
|
|
545
|
+
});
|
|
546
|
+
} else {
|
|
547
|
+
// Update dropdown position on vertical scroll
|
|
548
|
+
$(window).on('scroll.cms', function() {
|
|
549
|
+
if (self.wrapper.hasClass('open')) {
|
|
550
|
+
self.updateDropdownPosition();
|
|
551
|
+
}
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
// Global horizontal scroll handler - close dropdown on any horizontal scroll
|
|
556
|
+
// Listen for wheel events with horizontal delta
|
|
557
|
+
$(document).on('wheel.onescroll', function(e) {
|
|
558
|
+
if (!self.wrapper.hasClass('open')) {
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
// Check if horizontal scrolling (deltaX != 0)
|
|
563
|
+
if (e.originalEvent && Math.abs(e.originalEvent.deltaX) > 0) {
|
|
564
|
+
self.close();
|
|
565
|
+
}
|
|
566
|
+
});
|
|
567
|
+
|
|
568
|
+
// Also listen for scroll events on all elements to detect horizontal scroll
|
|
569
|
+
// Using MutationObserver to detect when elements with overflow scroll
|
|
570
|
+
self._detectHorizontalScroll = function() {
|
|
571
|
+
if (!self.wrapper.hasClass('open')) return;
|
|
572
|
+
|
|
573
|
+
// Check window horizontal scroll
|
|
574
|
+
if (window.scrollX !== self._lastWindowScrollX) {
|
|
575
|
+
self._lastWindowScrollX = window.scrollX;
|
|
576
|
+
if (self._lastWindowScrollX > 0) {
|
|
577
|
+
self.close();
|
|
578
|
+
return;
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
// Check all scrollable elements for horizontal scroll
|
|
583
|
+
var scrollableElements = document.querySelectorAll('*');
|
|
584
|
+
for (var i = 0; i < scrollableElements.length; i++) {
|
|
585
|
+
var el = scrollableElements[i];
|
|
586
|
+
var key = getElementKey(el);
|
|
587
|
+
|
|
588
|
+
if (self._elementScrollPositions[key] !== undefined) {
|
|
589
|
+
var currentScroll = el.scrollLeft;
|
|
590
|
+
if (currentScroll !== self._elementScrollPositions[key]) {
|
|
591
|
+
// Horizontal scroll detected
|
|
592
|
+
self.close();
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
};
|
|
598
|
+
|
|
599
|
+
// Store scroll positions and track changes
|
|
600
|
+
self._elementScrollPositions = {};
|
|
601
|
+
self._lastWindowScrollX = window.scrollX;
|
|
602
|
+
|
|
603
|
+
function getElementKey(el) {
|
|
604
|
+
if (el === document) return 'document';
|
|
605
|
+
if (el === document.documentElement) return 'html';
|
|
606
|
+
if (el === document.body) return 'body';
|
|
607
|
+
return el.tagName + '-' + (el.id || el.className || Math.random().toString(36).substr(2, 9));
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
// Override open to initialize tracking
|
|
611
|
+
var originalOpen = self.open.bind(self);
|
|
612
|
+
self.open = function() {
|
|
613
|
+
// Store initial scroll positions
|
|
614
|
+
self._elementScrollPositions = {};
|
|
615
|
+
self._lastWindowScrollX = window.scrollX;
|
|
616
|
+
|
|
617
|
+
var scrollableElements = document.querySelectorAll('*');
|
|
618
|
+
for (var i = 0; i < scrollableElements.length; i++) {
|
|
619
|
+
var el = scrollableElements[i];
|
|
620
|
+
if (el.scrollWidth > el.clientWidth || el.scrollHeight > el.clientHeight) {
|
|
621
|
+
self._elementScrollPositions[getElementKey(el)] = el.scrollLeft;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
// Start checking periodically
|
|
626
|
+
if (self._horizontalScrollInterval) {
|
|
627
|
+
clearInterval(self._horizontalScrollInterval);
|
|
628
|
+
}
|
|
629
|
+
self._horizontalScrollInterval = setInterval(function() {
|
|
630
|
+
self._detectHorizontalScroll();
|
|
631
|
+
}, 50);
|
|
632
|
+
|
|
633
|
+
originalOpen();
|
|
634
|
+
};
|
|
635
|
+
|
|
636
|
+
// Override close to stop tracking
|
|
637
|
+
var originalClose = self.close.bind(self);
|
|
638
|
+
self.close = function() {
|
|
639
|
+
if (self._horizontalScrollInterval) {
|
|
640
|
+
clearInterval(self._horizontalScrollInterval);
|
|
641
|
+
self._horizontalScrollInterval = null;
|
|
642
|
+
}
|
|
643
|
+
self._elementScrollPositions = {};
|
|
644
|
+
originalClose();
|
|
645
|
+
};
|
|
646
|
+
|
|
647
|
+
// Window click handler - close dropdown when clicking outside
|
|
648
|
+
$(window).on('click.ones', function(e) {
|
|
649
|
+
if (!self.settings.closeOnOutside) {
|
|
650
|
+
return;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
if (!self.wrapper.hasClass('open')) {
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
var $target = $(e.target);
|
|
658
|
+
// Don't close if clicked inside dropdown or wrapper elements
|
|
659
|
+
if ($target.closest('.cms-wrapper').length > 0 ||
|
|
660
|
+
$target.closest('.cms-dropdown').length > 0) {
|
|
661
|
+
return;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
// Submit form if submitOnOutside is enabled
|
|
665
|
+
if (self.settings.submitOnOutside) {
|
|
666
|
+
self.updateTriggerText();
|
|
667
|
+
if (self.settings.onOk) {
|
|
668
|
+
self.settings.onOk.call(self, self.getSelectedValues(), self.getSelectedLabels());
|
|
669
|
+
}
|
|
670
|
+
self.submitForm();
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
self.close();
|
|
674
|
+
});
|
|
675
|
+
|
|
676
|
+
this.optionsContainer.on('click', '.cms-option', function(e) {
|
|
677
|
+
e.stopPropagation();
|
|
678
|
+
|
|
679
|
+
var option = $(this);
|
|
680
|
+
var checkbox = option.find('input[type="checkbox"]');
|
|
681
|
+
|
|
682
|
+
if ($(e.target).is('input[type="checkbox"]')) {
|
|
683
|
+
return;
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
checkbox.prop('checked', !checkbox.prop('checked'));
|
|
687
|
+
self.handleOptionChange(option);
|
|
688
|
+
});
|
|
689
|
+
|
|
690
|
+
this.optionsContainer.on('change', 'input[type="checkbox"]', function(e) {
|
|
691
|
+
e.stopPropagation();
|
|
692
|
+
var option = $(e.target).closest('.cms-option');
|
|
693
|
+
self.handleOptionChange(option);
|
|
694
|
+
});
|
|
695
|
+
|
|
696
|
+
this.okBtn.on('click', function(e) {
|
|
697
|
+
e.stopPropagation();
|
|
698
|
+
self.handleOk();
|
|
699
|
+
});
|
|
700
|
+
|
|
701
|
+
this.cancelBtn.on('click', function(e) {
|
|
702
|
+
e.stopPropagation();
|
|
703
|
+
self.handleCancel();
|
|
704
|
+
});
|
|
705
|
+
},
|
|
706
|
+
|
|
707
|
+
handleOptionChange: function(option) {
|
|
708
|
+
var value = option.data('value');
|
|
709
|
+
|
|
710
|
+
if (value === 'select-all') {
|
|
711
|
+
var checkbox = option.find('input[type="checkbox"]');
|
|
712
|
+
this.handleSelectAll(checkbox.prop('checked'));
|
|
713
|
+
} else {
|
|
714
|
+
var checkbox = option.find('input[type="checkbox"]');
|
|
715
|
+
if (checkbox.prop('checked')) {
|
|
716
|
+
option.addClass('selected');
|
|
717
|
+
} else {
|
|
718
|
+
option.removeClass('selected');
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
var self = this;
|
|
722
|
+
setTimeout(function() {
|
|
723
|
+
self.updateSelectAllState();
|
|
724
|
+
self.updateTriggerText();
|
|
725
|
+
}, 0);
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
this.updateHiddenInputs();
|
|
729
|
+
|
|
730
|
+
if (this.settings.onChange) {
|
|
731
|
+
this.settings.onChange.call(this, this.getSelectedValues(), this.getSelectedLabels());
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
if (this.settings.onSelect) {
|
|
735
|
+
this.settings.onSelect.call(this, this.getSelectedValues());
|
|
736
|
+
}
|
|
737
|
+
},
|
|
738
|
+
|
|
739
|
+
handleSelectAll: function(checked) {
|
|
740
|
+
var self = this;
|
|
741
|
+
this.optionsContainer.find('.cms-option:not([data-value="select-all"])').each(function() {
|
|
742
|
+
var option = $(this);
|
|
743
|
+
option.find('input[type="checkbox"]').prop('checked', checked);
|
|
744
|
+
if (checked) {
|
|
745
|
+
option.addClass('selected');
|
|
746
|
+
} else {
|
|
747
|
+
option.removeClass('selected');
|
|
748
|
+
}
|
|
749
|
+
});
|
|
750
|
+
|
|
751
|
+
this.updateSelectAllState();
|
|
752
|
+
this.updateTriggerText();
|
|
753
|
+
this.updateHiddenInputs();
|
|
754
|
+
},
|
|
755
|
+
|
|
756
|
+
updateSelectAllState: function() {
|
|
757
|
+
var allOptions = this.optionsContainer.find('.cms-option:not([data-value="select-all"]) input[type="checkbox"]');
|
|
758
|
+
var checkedOptions = allOptions.filter(':checked');
|
|
759
|
+
var totalCount = allOptions.length;
|
|
760
|
+
var checkedCount = checkedOptions.length;
|
|
761
|
+
|
|
762
|
+
var selectAllCheckbox = this.optionsContainer.find('.cms-option[data-value="select-all"] input[type="checkbox"]');
|
|
763
|
+
|
|
764
|
+
selectAllCheckbox.prop('indeterminate', false);
|
|
765
|
+
selectAllCheckbox.prop('checked', false);
|
|
766
|
+
|
|
767
|
+
if (checkedCount === 0) {
|
|
768
|
+
// Nothing selected
|
|
769
|
+
} else if (checkedCount === totalCount && totalCount > 0) {
|
|
770
|
+
selectAllCheckbox.prop('checked', true);
|
|
771
|
+
} else {
|
|
772
|
+
selectAllCheckbox.prop('indeterminate', true);
|
|
773
|
+
}
|
|
774
|
+
},
|
|
775
|
+
|
|
776
|
+
getSelectedValues: function() {
|
|
777
|
+
var values = [];
|
|
778
|
+
this.optionsContainer.find('.cms-option:not([data-value="select-all"]) input[type="checkbox"]:checked')
|
|
779
|
+
.each(function() {
|
|
780
|
+
var val = $(this).val();
|
|
781
|
+
if (!isNaN(val) && val !== '') {
|
|
782
|
+
val = Number(val);
|
|
783
|
+
}
|
|
784
|
+
values.push(val);
|
|
785
|
+
});
|
|
786
|
+
return values;
|
|
787
|
+
},
|
|
788
|
+
|
|
789
|
+
getSelectedLabels: function() {
|
|
790
|
+
var labels = [];
|
|
791
|
+
this.optionsContainer.find('.cms-option:not([data-value="select-all"]) input[type="checkbox"]:checked')
|
|
792
|
+
.siblings('label')
|
|
793
|
+
.each(function() {
|
|
794
|
+
labels.push($(this).text());
|
|
795
|
+
});
|
|
796
|
+
return labels;
|
|
797
|
+
},
|
|
798
|
+
|
|
799
|
+
updateTriggerText: function() {
|
|
800
|
+
var labels = this.getSelectedLabels();
|
|
801
|
+
var values = this.getSelectedValues();
|
|
802
|
+
var textSpan = this.trigger.find('.cms-selected-text');
|
|
803
|
+
|
|
804
|
+
if (labels.length === 0) {
|
|
805
|
+
textSpan.empty().text(this.settings.placeholder).addClass('cms-placeholder');
|
|
806
|
+
} else if (this.settings.showBadges) {
|
|
807
|
+
textSpan.empty().removeClass('cms-placeholder');
|
|
808
|
+
|
|
809
|
+
var self = this;
|
|
810
|
+
$.each(values, function(index, value) {
|
|
811
|
+
var badge = $('<span class="cms-badge"></span>');
|
|
812
|
+
var labelSpan = $('<span></span>').text(labels[index]);
|
|
813
|
+
var removeBtn = $('<button type="button" class="cms-badge-remove">×</button>');
|
|
814
|
+
|
|
815
|
+
removeBtn.on('click', function(e) {
|
|
816
|
+
e.stopPropagation();
|
|
817
|
+
self.unselect(value);
|
|
818
|
+
});
|
|
819
|
+
|
|
820
|
+
badge.append(labelSpan);
|
|
821
|
+
badge.append(removeBtn);
|
|
822
|
+
textSpan.append(badge);
|
|
823
|
+
});
|
|
824
|
+
} else {
|
|
825
|
+
textSpan.empty().removeClass('cms-placeholder');
|
|
826
|
+
textSpan.text(labels.length + ' items selected');
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
this.updateExternalBadges(values, labels);
|
|
830
|
+
},
|
|
831
|
+
|
|
832
|
+
updateExternalBadges: function(values, labels) {
|
|
833
|
+
if (!this.settings.showBadgesExternal) {
|
|
834
|
+
return;
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
var $externalContainer = $('#' + this.settings.showBadgesExternal);
|
|
838
|
+
|
|
839
|
+
if ($externalContainer.length === 0) {
|
|
840
|
+
console.warn('OneSelect: External container not found - #' + this.settings.showBadgesExternal);
|
|
841
|
+
return;
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
$externalContainer.empty();
|
|
845
|
+
|
|
846
|
+
if (values.length === 0) {
|
|
847
|
+
return;
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
var self = this;
|
|
851
|
+
$.each(values, function(index, value) {
|
|
852
|
+
var badge = $('<span class="cms-badge"></span>');
|
|
853
|
+
var labelSpan = $('<span></span>').text(labels[index]);
|
|
854
|
+
var removeBtn = $('<button type="button" class="cms-badge-remove">×</button>');
|
|
855
|
+
|
|
856
|
+
removeBtn.on('click', function(e) {
|
|
857
|
+
e.preventDefault();
|
|
858
|
+
e.stopPropagation();
|
|
859
|
+
self.unselect(value);
|
|
860
|
+
});
|
|
861
|
+
|
|
862
|
+
badge.append(labelSpan);
|
|
863
|
+
badge.append(removeBtn);
|
|
864
|
+
$externalContainer.append(badge);
|
|
865
|
+
});
|
|
866
|
+
},
|
|
867
|
+
|
|
868
|
+
toggle: function() {
|
|
869
|
+
if (this.wrapper.hasClass('open')) {
|
|
870
|
+
this.close();
|
|
871
|
+
} else {
|
|
872
|
+
this.open();
|
|
873
|
+
}
|
|
874
|
+
},
|
|
875
|
+
|
|
876
|
+
open: function() {
|
|
877
|
+
// Close other open dropdowns
|
|
878
|
+
$('.cms-wrapper.open').not(this.wrapper).removeClass('open');
|
|
879
|
+
$('.cms-dropdown.open').not(this.dropdown).removeClass('open');
|
|
880
|
+
|
|
881
|
+
// Calculate position
|
|
882
|
+
this.updateDropdownPosition();
|
|
883
|
+
|
|
884
|
+
this.wrapper.addClass('open');
|
|
885
|
+
this.dropdown.addClass('open');
|
|
886
|
+
},
|
|
887
|
+
|
|
888
|
+
updateDropdownPosition: function() {
|
|
889
|
+
var rect = this.wrapper[0].getBoundingClientRect();
|
|
890
|
+
var wrapperHeight = this.wrapper.outerHeight();
|
|
891
|
+
var wrapperWidth = this.wrapper.outerWidth();
|
|
892
|
+
|
|
893
|
+
this.dropdown.css({
|
|
894
|
+
position: 'fixed',
|
|
895
|
+
top: rect.bottom + 'px',
|
|
896
|
+
left: rect.left + 'px',
|
|
897
|
+
width: wrapperWidth + 'px'
|
|
898
|
+
});
|
|
899
|
+
},
|
|
900
|
+
|
|
901
|
+
close: function() {
|
|
902
|
+
this.wrapper.removeClass('open');
|
|
903
|
+
this.dropdown.removeClass('open');
|
|
904
|
+
},
|
|
905
|
+
|
|
906
|
+
handleOk: function() {
|
|
907
|
+
this.updateTriggerText();
|
|
908
|
+
|
|
909
|
+
var values = this.getSelectedValues();
|
|
910
|
+
var labels = this.getSelectedLabels();
|
|
911
|
+
|
|
912
|
+
if (this.settings.onOk) {
|
|
913
|
+
this.settings.onOk.call(this, values, labels);
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
if (this.settings.submitForm) {
|
|
917
|
+
this.submitForm();
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
this.close();
|
|
921
|
+
},
|
|
922
|
+
|
|
923
|
+
submitForm: function() {
|
|
924
|
+
var form = null;
|
|
925
|
+
|
|
926
|
+
if (this.settings.formId) {
|
|
927
|
+
form = $('#' + this.settings.formId);
|
|
928
|
+
if (form.length === 0) {
|
|
929
|
+
console.warn('OneSelect: Form with ID "' + this.settings.formId + '" not found');
|
|
930
|
+
return;
|
|
931
|
+
}
|
|
932
|
+
} else {
|
|
933
|
+
form = this.$element.closest('form');
|
|
934
|
+
if (form.length === 0) {
|
|
935
|
+
console.warn('OneSelect: No parent form found');
|
|
936
|
+
return;
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
form[0].submit();
|
|
941
|
+
},
|
|
942
|
+
|
|
943
|
+
handleCancel: function() {
|
|
944
|
+
this.settings.selectedValues = [];
|
|
945
|
+
this.optionsContainer.find('input[type="checkbox"]').prop('checked', false);
|
|
946
|
+
this.optionsContainer.find('.cms-option').removeClass('selected');
|
|
947
|
+
this.updateSelectAllState();
|
|
948
|
+
this.updateTriggerText();
|
|
949
|
+
this.updateHiddenInputs();
|
|
950
|
+
|
|
951
|
+
if (this.settings.onCancel) {
|
|
952
|
+
this.settings.onCancel.call(this);
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
if (this.settings.submitForm) {
|
|
956
|
+
this.submitForm();
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
this.close();
|
|
960
|
+
},
|
|
961
|
+
|
|
962
|
+
setValue: function(values) {
|
|
963
|
+
this.settings.selectedValues = values || [];
|
|
964
|
+
this.renderOptions();
|
|
965
|
+
this.updateTriggerText();
|
|
966
|
+
this.updateHiddenInputs();
|
|
967
|
+
},
|
|
968
|
+
|
|
969
|
+
getValue: function() {
|
|
970
|
+
return this.getSelectedValues();
|
|
971
|
+
},
|
|
972
|
+
|
|
973
|
+
updateData: function(data) {
|
|
974
|
+
this.settings.data = data || [];
|
|
975
|
+
this.settings.selectedValues = [];
|
|
976
|
+
this.renderOptions();
|
|
977
|
+
this.updateTriggerText();
|
|
978
|
+
this.updateHiddenInputs();
|
|
979
|
+
},
|
|
980
|
+
|
|
981
|
+
loadData: function(customAjaxConfig, onSuccess, onError) {
|
|
982
|
+
var self = this;
|
|
983
|
+
var ajaxConfig = customAjaxConfig || this.settings.ajax;
|
|
984
|
+
|
|
985
|
+
if (!ajaxConfig || !ajaxConfig.url) {
|
|
986
|
+
console.error('OneSelect: Ajax configuration or url is missing');
|
|
987
|
+
return;
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
if (this.settings.beforeLoad) {
|
|
991
|
+
this.settings.beforeLoad.call(this);
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
this.trigger.find('.cms-selected-text').text('Loading...');
|
|
995
|
+
|
|
996
|
+
var request = $.extend(true, {
|
|
997
|
+
url: ajaxConfig.url,
|
|
998
|
+
method: ajaxConfig.method || 'GET',
|
|
999
|
+
data: ajaxConfig.data || {},
|
|
1000
|
+
dataType: ajaxConfig.dataType || 'json',
|
|
1001
|
+
success: function(response) {
|
|
1002
|
+
var data = response;
|
|
1003
|
+
if (typeof response === 'object' && response.data) {
|
|
1004
|
+
data = response.data;
|
|
1005
|
+
} else if (typeof response === 'object' && response.results) {
|
|
1006
|
+
data = response.results;
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
self.settings.data = data || [];
|
|
1010
|
+
self.renderOptions();
|
|
1011
|
+
self.updateTriggerText();
|
|
1012
|
+
|
|
1013
|
+
if (self.settings.afterLoad) {
|
|
1014
|
+
self.settings.afterLoad.call(self, data, response);
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
if (onSuccess) {
|
|
1018
|
+
onSuccess.call(self, data, response);
|
|
1019
|
+
}
|
|
1020
|
+
},
|
|
1021
|
+
error: function(xhr, status, error) {
|
|
1022
|
+
self.trigger.find('.cms-selected-text').text('Error loading data');
|
|
1023
|
+
|
|
1024
|
+
if (self.settings.onLoadError) {
|
|
1025
|
+
self.settings.onLoadError.call(self, xhr, status, error);
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
if (onError) {
|
|
1029
|
+
onError.call(self, xhr, status, error);
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
}, ajaxConfig);
|
|
1033
|
+
|
|
1034
|
+
if (ajaxConfig.success) {
|
|
1035
|
+
var originalSuccess = request.success;
|
|
1036
|
+
request.success = function(response) {
|
|
1037
|
+
ajaxConfig.success(response);
|
|
1038
|
+
originalSuccess(response);
|
|
1039
|
+
};
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
if (ajaxConfig.error) {
|
|
1043
|
+
var originalError = request.error;
|
|
1044
|
+
request.error = function(xhr, status, error) {
|
|
1045
|
+
ajaxConfig.error(xhr, status, error);
|
|
1046
|
+
originalError(xhr, status, error);
|
|
1047
|
+
};
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
$.ajax(request);
|
|
1051
|
+
},
|
|
1052
|
+
|
|
1053
|
+
reload: function() {
|
|
1054
|
+
if (this.settings.ajax) {
|
|
1055
|
+
this.loadData();
|
|
1056
|
+
} else {
|
|
1057
|
+
console.warn('OneSelect: No ajax configuration found');
|
|
1058
|
+
}
|
|
1059
|
+
},
|
|
1060
|
+
|
|
1061
|
+
select: function(value) {
|
|
1062
|
+
var checkbox = this.optionsContainer.find('.cms-option:not([data-value="select-all"]) input[type="checkbox"][value="' + value + '"]');
|
|
1063
|
+
if (checkbox.length) {
|
|
1064
|
+
checkbox.prop('checked', true);
|
|
1065
|
+
checkbox.closest('.cms-option').addClass('selected');
|
|
1066
|
+
this.updateSelectAllState();
|
|
1067
|
+
this.updateTriggerText();
|
|
1068
|
+
this.updateHiddenInputs();
|
|
1069
|
+
}
|
|
1070
|
+
},
|
|
1071
|
+
|
|
1072
|
+
unselect: function(value) {
|
|
1073
|
+
var checkbox = this.optionsContainer.find('.cms-option:not([data-value="select-all"]) input[type="checkbox"][value="' + value + '"]');
|
|
1074
|
+
if (checkbox.length) {
|
|
1075
|
+
checkbox.prop('checked', false);
|
|
1076
|
+
checkbox.closest('.cms-option').removeClass('selected');
|
|
1077
|
+
this.updateSelectAllState();
|
|
1078
|
+
this.updateTriggerText();
|
|
1079
|
+
this.updateHiddenInputs();
|
|
1080
|
+
}
|
|
1081
|
+
},
|
|
1082
|
+
|
|
1083
|
+
selectAll: function() {
|
|
1084
|
+
this.handleSelectAll(true);
|
|
1085
|
+
},
|
|
1086
|
+
|
|
1087
|
+
unselectAll: function() {
|
|
1088
|
+
this.handleSelectAll(false);
|
|
1089
|
+
},
|
|
1090
|
+
|
|
1091
|
+
toggleSelection: function(value) {
|
|
1092
|
+
var checkbox = this.optionsContainer.find('.cms-option:not([data-value="select-all"]) input[type="checkbox"][value="' + value + '"]');
|
|
1093
|
+
if (checkbox.length) {
|
|
1094
|
+
var isChecked = checkbox.prop('checked');
|
|
1095
|
+
checkbox.prop('checked', !isChecked);
|
|
1096
|
+
|
|
1097
|
+
var option = checkbox.closest('.cms-option');
|
|
1098
|
+
if (!isChecked) {
|
|
1099
|
+
option.addClass('selected');
|
|
1100
|
+
} else {
|
|
1101
|
+
option.removeClass('selected');
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
this.updateSelectAllState();
|
|
1105
|
+
this.updateTriggerText();
|
|
1106
|
+
this.updateHiddenInputs();
|
|
1107
|
+
}
|
|
1108
|
+
},
|
|
1109
|
+
|
|
1110
|
+
getInstanceId: function() {
|
|
1111
|
+
return this.instanceId;
|
|
1112
|
+
},
|
|
1113
|
+
|
|
1114
|
+
destroy: function() {
|
|
1115
|
+
delete instances[this.instanceId];
|
|
1116
|
+
|
|
1117
|
+
$(window).off('.cms');
|
|
1118
|
+
$(window).off('.ones');
|
|
1119
|
+
$(document).off('.onescroll');
|
|
1120
|
+
this.trigger.off();
|
|
1121
|
+
this.okBtn.off();
|
|
1122
|
+
this.cancelBtn.off();
|
|
1123
|
+
this.optionsContainer.off();
|
|
1124
|
+
|
|
1125
|
+
// Clear horizontal scroll tracking interval
|
|
1126
|
+
if (this._horizontalScrollInterval) {
|
|
1127
|
+
clearInterval(this._horizontalScrollInterval);
|
|
1128
|
+
this._horizontalScrollInterval = null;
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
$('input.cms-hidden-input[data-cms-input="' + this.settings.name + '"]').remove();
|
|
1132
|
+
|
|
1133
|
+
this.wrapper.remove();
|
|
1134
|
+
this.dropdown.remove();
|
|
1135
|
+
this.$element.removeData(pluginName);
|
|
1136
|
+
}
|
|
1137
|
+
};
|
|
1138
|
+
|
|
1139
|
+
/**
|
|
1140
|
+
* Get instance by ID
|
|
1141
|
+
*/
|
|
1142
|
+
OneSelect.getInstance = function(instanceId) {
|
|
1143
|
+
return instances[instanceId] || null;
|
|
1144
|
+
};
|
|
1145
|
+
|
|
1146
|
+
/**
|
|
1147
|
+
* Get all instances
|
|
1148
|
+
*/
|
|
1149
|
+
OneSelect.getAllInstances = function() {
|
|
1150
|
+
return instances;
|
|
1151
|
+
};
|
|
1152
|
+
|
|
1153
|
+
/**
|
|
1154
|
+
* jQuery Plugin Registration
|
|
1155
|
+
*/
|
|
1156
|
+
$.fn[pluginName] = function(options) {
|
|
1157
|
+
var args = arguments;
|
|
1158
|
+
var returnValue = this;
|
|
1159
|
+
|
|
1160
|
+
this.each(function() {
|
|
1161
|
+
var $this = $(this);
|
|
1162
|
+
var instance = $this.data(pluginName);
|
|
1163
|
+
|
|
1164
|
+
// Initialize if not already
|
|
1165
|
+
if (!instance) {
|
|
1166
|
+
// Don't auto-initialize - only init if options are provided
|
|
1167
|
+
if (typeof options === 'object' || !options) {
|
|
1168
|
+
instance = new OneSelect(this, options);
|
|
1169
|
+
$this.data(pluginName, instance);
|
|
1170
|
+
} else {
|
|
1171
|
+
// Method called without initialization
|
|
1172
|
+
return;
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
// Call method
|
|
1177
|
+
if (typeof options === 'string') {
|
|
1178
|
+
if (options === 'value') {
|
|
1179
|
+
if (args[1] !== undefined) {
|
|
1180
|
+
instance.setValue(args[1]);
|
|
1181
|
+
returnValue = $this;
|
|
1182
|
+
} else {
|
|
1183
|
+
returnValue = instance.getValue();
|
|
1184
|
+
}
|
|
1185
|
+
} else if (options === 'getValues') {
|
|
1186
|
+
returnValue = instance.getSelectedValues();
|
|
1187
|
+
} else if (options === 'getLabels') {
|
|
1188
|
+
returnValue = instance.getSelectedLabels();
|
|
1189
|
+
} else if (options === 'getInstanceId') {
|
|
1190
|
+
returnValue = instance.getInstanceId();
|
|
1191
|
+
} else if (options === 'updateData') {
|
|
1192
|
+
instance.updateData(args[1]);
|
|
1193
|
+
} else if (options === 'loadData') {
|
|
1194
|
+
instance.loadData(args[1], args[2], args[3]);
|
|
1195
|
+
} else if (options === 'reload') {
|
|
1196
|
+
instance.reload();
|
|
1197
|
+
} else if (options === 'select') {
|
|
1198
|
+
instance.select(args[1]);
|
|
1199
|
+
} else if (options === 'unselect') {
|
|
1200
|
+
instance.unselect(args[1]);
|
|
1201
|
+
} else if (options === 'selectAll') {
|
|
1202
|
+
instance.selectAll();
|
|
1203
|
+
} else if (options === 'unselectAll') {
|
|
1204
|
+
instance.unselectAll();
|
|
1205
|
+
} else if (options === 'toggleSelection') {
|
|
1206
|
+
instance.toggleSelection(args[1]);
|
|
1207
|
+
} else if (options === 'open') {
|
|
1208
|
+
instance.open();
|
|
1209
|
+
} else if (options === 'close') {
|
|
1210
|
+
instance.close();
|
|
1211
|
+
} else if (options === 'destroy') {
|
|
1212
|
+
instance.destroy();
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
});
|
|
1216
|
+
|
|
1217
|
+
return returnValue;
|
|
1218
|
+
};
|
|
1219
|
+
|
|
1220
|
+
// Expose constructor
|
|
1221
|
+
$.fn[pluginName].Constructor = OneSelect;
|
|
1222
|
+
|
|
1223
|
+
// Expose static methods
|
|
1224
|
+
$.fn[pluginName].getInstance = OneSelect.getInstance;
|
|
1225
|
+
$.fn[pluginName].getAllInstances = OneSelect.getAllInstances;
|
|
1226
|
+
|
|
1227
|
+
}));
|