@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 ADDED
@@ -0,0 +1,848 @@
1
+ # 🎯 OneSelect - jQuery Multi-Select Dropdown Component
2
+
3
+ A powerful, flexible, and feature-rich multi-select dropdown component for jQuery.
4
+
5
+ ## 📋 Table of Contents
6
+
7
+ 1. [Overview](#overview)
8
+ 2. [Features](#features)
9
+ 3. [Installation](#installation)
10
+ 4. [All Parameters](#all-parameters)
11
+ 5. [Data Attributes](#data-attributes)
12
+ 6. [Methods](#methods)
13
+ 7. [Callbacks](#callbacks)
14
+ 8. [Examples](#examples)
15
+ 9. [AJAX Integration](#ajax-integration)
16
+ 10. [Form Submission](#form-submission)
17
+ 11. [Badge System](#badge-system)
18
+ 12. [CSS Styling](#css-styling)
19
+
20
+ ---
21
+
22
+ ## 🎯 Overview
23
+
24
+ OneSelect is a powerful **jQuery-based** plugin that provides multi-select functionality with comprehensive customization options.
25
+
26
+ ### 🚀 Key Features
27
+
28
+ - ✅ **Multiple Selection** - Select multiple items with checkboxes
29
+ - 🎯 **Select All** - Select all items with one click
30
+ - 🏷️️ **Badge System** - Display selected items as badges
31
+ - 📤 **External Badges** - Display badges in external elements
32
+ - 🔄 **AJAX Support** - Load data dynamically
33
+ - 🔍 **Search Feature** - Local filtering or AJAX search with debounce
34
+ - 📝 **Form Submission** - Submit data via hidden inputs
35
+ - 🎨 **Fully Customizable** - Complete control with 27+ parameters
36
+ - 📱 **Responsive** - Works on all devices
37
+ - 🌐 **Data Attributes** - Configure via HTML attributes
38
+ - 🎪 **Multiple Instances** - Independent selects on same page
39
+ - 🌪 **Click Outside** - Close dropdown when clicking outside (default: true)
40
+ - 📍 **Smart Positioning** - Dropdown positioned with `position: fixed` using viewport coordinates
41
+ - 🔀 **Horizontal Scroll Detection** - Automatically closes on any horizontal scroll to prevent misalignment
42
+
43
+ ---
44
+
45
+ ## 💡 Features
46
+
47
+ ### 📊 Technical Stack
48
+
49
+ | Component | Technology |
50
+ |-----------|------------|
51
+ | **Library** | jQuery (required dependency) |
52
+ | **Plugin Type** | jQuery Plugin |
53
+ | **Files** | `one-select.js`, `one-select.min.css` |
54
+
55
+ ### 🎯 Functionality
56
+
57
+ ```
58
+ 📦 Data Structures:
59
+ ├── String Array: ['Apple', 'Banana', 'Cherry']
60
+ └── Object Array: [{id: 1, name: 'Apple'}, ...]
61
+
62
+ 🎮 Interactions:
63
+ ├── Click to select (checkbox)
64
+ ├── "Select All" option
65
+ ├── OK button (confirm)
66
+ ├── Cancel button (clears selection and closes)
67
+ └── × button (remove from badges)
68
+
69
+ 📤 Data Flow:
70
+ ├── onChange(values, labels) - When selection changes
71
+ ├── onOk(values, labels) - When OK clicked
72
+ ├── onCancel() - When Cancel clicked
73
+ └── AJAX callbacks (beforeLoad, afterLoad, onLoadError)
74
+ ```
75
+
76
+ ---
77
+
78
+ ## 📦 Installation
79
+
80
+ ### 1. Dependencies
81
+
82
+ ```html
83
+ <!-- jQuery must be included first -->
84
+ <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
85
+ ```
86
+
87
+ ### 2. Include Files
88
+
89
+ ```html
90
+ <!-- CSS -->
91
+ <link rel="stylesheet" href="/path/to/one-select.min.css">
92
+
93
+ <!-- JavaScript -->
94
+ <script src="/path/to/one-select.js"></script>
95
+ ```
96
+
97
+ ### 3. Create HTML Element
98
+
99
+ ```html
100
+ <div id="mySelect"></div>
101
+ ```
102
+
103
+ ### 4. Initialize
104
+
105
+ ```javascript
106
+ $('#mySelect').oneSelect({
107
+ data: ['Apple', 'Banana', 'Cherry']
108
+ });
109
+ ```
110
+
111
+ ---
112
+
113
+ ## ⚙️ All Parameters
114
+
115
+ | Parameter | Type | Default | Description |
116
+ |---------|-----|---------|-------------|
117
+ | `placeholder` | String | `'Select options...'` | Placeholder text when nothing selected |
118
+ | `selectAllText` | String | `'Select All'` | "Select All" button text |
119
+ | `okText` | String | `'OK'` | OK button text |
120
+ | `cancelText` | String | `'Cancel'` | Cancel button text |
121
+ | `data` | Array | `[]` | Options list (string or object array) |
122
+ | `selectedValues` | Array | `[]` | Initially selected values |
123
+ | `value` | String/Array/null | `null` | Single or array value - pre-selects items |
124
+ | `valueField` | String | `'value'` | Value field name in object array |
125
+ | `labelField` | String | `'label'` | Label field name in object array |
126
+ | `showCheckbox` | Boolean | `true` | Show/hide checkboxes |
127
+ | `showBadges` | Boolean | `false` | Show badges in trigger |
128
+ | `showBadgesExternal` | String/null | `null` | External element ID (for badges) |
129
+ | `showSearch` | Boolean | `false` | Show search input in dropdown |
130
+ | `searchPlaceholder` | String | `'Search...'` | Search input placeholder text |
131
+ | `searchUrl` | String/null | `null` | URL for AJAX search (GET request with `q` parameter) |
132
+ | `searchDebounceDelay` | Number | `300` | Delay in milliseconds for search debounce |
133
+ | `closeOnScroll` | Boolean | `false` | Close dropdown on page scroll |
134
+ | `closeOnOutside` | Boolean | `true` | Close dropdown when clicking outside |
135
+ | `submitForm` | Boolean | `false` | Submit form on OK click |
136
+ | `submitOnOutside` | Boolean | `false` | Submit form on outside click |
137
+ | `formId` | String/null | `null` | Specific form ID (null: parent form) |
138
+ | `name` | String/null | `null` | Hidden input name attribute |
139
+ | `multiple` | Boolean | `true` | Submit as array (name[]) |
140
+ | `ajax` | Object/null | `null` | AJAX configuration object |
141
+ | `autoLoad` | Boolean | `true` | Auto load data via AJAX |
142
+ | `beforeLoad` | Function/null | `null` | Called before AJAX |
143
+ | `afterLoad` | Function/null | `null` | Called after AJAX success |
144
+ | `onLoadError` | Function/null | `null` | Called on AJAX error |
145
+ | `onChange` | Function/null | `null` | Called when selection changes |
146
+ | `onSelect` | Function/null | `null` | Previous version of onChange |
147
+ | `onOk` | Function/null | `null` | Called when OK clicked |
148
+ | `onCancel` | Function/null | `null` | Called when Cancel clicked |
149
+
150
+ ---
151
+
152
+ ## 🏷️ Data Attributes
153
+
154
+ All parameters can be set via HTML data attributes. Data attributes **override JS parameters**.
155
+
156
+ | Data Attribute | Parameter | Type | Example |
157
+ |----------------|----------|-----|---------|
158
+ | `data-ones-placeholder` | `placeholder` | String | `data-ones-placeholder="Select..."` |
159
+ | `data-ones-select-all-text` | `selectAllText` | String | `data-ones-select-all-text="Select All"` |
160
+ | `data-ones-ok-text` | `okText` | String | `data-ones-ok-text="Confirm"` |
161
+ | `data-ones-cancel-text` | `cancelText` | String | `data-ones-cancel-text="Cancel"` |
162
+ | `data-ones-data` | `data` | Array | `data-ones-data='["A","B","C"]'` |
163
+ | `data-ones-selected` | `selectedValues` | Array | `data-ones-selected='["A","C"]'` |
164
+ | `data-ones-value` | `value` | String/Array | `data-ones-value='"A"'` or `data-ones-value='["A","C"]'` |
165
+ | `data-ones-value-field` | `valueField` | String | `data-ones-value-field="id"` |
166
+ | `data-ones-label-field` | `labelField` | String | `data-ones-label-field="name"` |
167
+ | `data-ones-name` | `name` | String | `data-ones-name="items"` |
168
+ | `data-ones-multiple` | `multiple` | Boolean | `data-ones-multiple="true"` |
169
+ | `data-ones-show-checkbox` | `showCheckbox` | Boolean | `data-ones-show-checkbox="false"` |
170
+ | `data-ones-show-badges` | `showBadges` | Boolean | `data-ones-show-badges="true"` |
171
+ | `data-ones-show-badges-external` | `showBadgesExternal` | String | `data-ones-show-badges-external="badgesDiv"` |
172
+ | `data-ones-show-search` | `showSearch` | Boolean | `data-ones-show-search="true"` |
173
+ | `data-ones-search-placeholder` | `searchPlaceholder` | String | `data-ones-search-placeholder="Search items..."` |
174
+ | `data-ones-search-url` | `searchUrl` | String | `data-ones-search-url="/api/search"` |
175
+ | `data-ones-search-debounce-delay` | `searchDebounceDelay` | Number | `data-ones-search-debounce-delay="500"` |
176
+ | `data-ones-close-on-scroll` | `closeOnScroll` | Boolean | `data-ones-close-on-scroll="true"` |
177
+ | `data-ones-close-on-outside` | `closeOnOutside` | Boolean | `data-ones-close-on-outside="true"` |
178
+ | `data-ones-submit-form` | `submitForm` | Boolean | `data-ones-submit-form="true"` |
179
+ | `data-ones-submit-on-outside` | `submitOnOutside` | Boolean | `data-ones-submit-on-outside="true"` |
180
+ | `data-ones-form-id` | `formId` | String | `data-ones-form-id="myForm"` |
181
+ | `data-ones-auto-load` | `autoLoad` | Boolean | `data-ones-auto-load="false"` |
182
+ | `data-ones-ajax` | `ajax` | Object | `data-ones-ajax='{"url": "/api/items"}'` |
183
+
184
+ ### Example:
185
+
186
+ ```html
187
+ <div id="mySelect"
188
+ data-ones-placeholder="Select products..."
189
+ data-ones-data='["Apple", "Banana", "Cherry"]'
190
+ data-ones-show-badges="true"
191
+ data-ones-name="fruits"
192
+ ></div>
193
+
194
+ <script>
195
+ $('#mySelect').oneSelect({
196
+ // JS options (data attributes override these)
197
+ });
198
+ </script>
199
+ ```
200
+
201
+ ---
202
+
203
+ ## 🔧 Methods
204
+
205
+ Call via jQuery plugin method:
206
+
207
+ ```javascript
208
+ // Get selected values
209
+ var values = $('#mySelect').oneSelect('getValues');
210
+ var labels = $('#mySelect').oneSelect('getLabels');
211
+
212
+ // Set selection
213
+ $('#mySelect').oneSelect('value', ['Apple', 'Banana']);
214
+
215
+ // Update data
216
+ $('#mySelect').oneSelect('updateData', ['New', 'Data']);
217
+
218
+ // Load data via AJAX
219
+ $('#mySelect').oneSelect('loadData');
220
+
221
+ // Selection control
222
+ $('#mySelect').oneSelect('selectAll');
223
+ $('#mySelect').oneSelect('unselectAll');
224
+ $('#mySelect').oneSelect('select', 'Apple');
225
+ $('#mySelect').oneSelect('unselect', 'Banana');
226
+ $('#mySelect').oneSelect('toggleSelection', 'Cherry');
227
+
228
+ // Dropdown control
229
+ $('#mySelect').oneSelect('open');
230
+ $('#mySelect').oneSelect('close');
231
+
232
+ // Get instance ID
233
+ var instanceId = $('#mySelect').oneSelect('getInstanceId');
234
+
235
+ // Get instance object
236
+ var instance = OneSelect.getInstance(instanceId);
237
+
238
+ // Get all instances
239
+ var allInstances = OneSelect.getAllInstances();
240
+
241
+ // Destroy
242
+ $('#mySelect').oneSelect('destroy');
243
+ ```
244
+
245
+ ---
246
+
247
+ ## 🎯 Callbacks
248
+
249
+ ### onChange(values, labels)
250
+
251
+ Called every time selection changes. Most important callback.
252
+
253
+ ```javascript
254
+ $('#mySelect').oneSelect({
255
+ data: ['A', 'B', 'C'],
256
+ onChange: function(values, labels) {
257
+ console.log('Values:', values);
258
+ console.log('Labels:', labels);
259
+ // values: ['A', 'C']
260
+ // labels: ['A', 'C']
261
+ }
262
+ });
263
+ ```
264
+
265
+ ### onOk(values, labels)
266
+
267
+ Called when OK button is clicked.
268
+
269
+ ```javascript
270
+ $('#mySelect').oneSelect({
271
+ onOk: function(values, labels) {
272
+ alert('Selected: ' + labels.join(', '));
273
+ }
274
+ });
275
+ ```
276
+
277
+ ### onCancel()
278
+
279
+ Called when Cancel button is clicked and clears all selections.
280
+
281
+ ```javascript
282
+ $('#mySelect').oneSelect({
283
+ onCancel: function() {
284
+ console.log('Selection cancelled');
285
+ }
286
+ });
287
+ ```
288
+
289
+ ### AJAX Callbacks
290
+
291
+ ```javascript
292
+ $('#mySelect').oneSelect({
293
+ ajax: {
294
+ url: '/api/items',
295
+ method: 'GET'
296
+ },
297
+ beforeLoad: function() {
298
+ $('#loading').show();
299
+ },
300
+ afterLoad: function(response) {
301
+ $('#loading').hide();
302
+ console.log('Data loaded:', response);
303
+ },
304
+ onLoadError: function(error) {
305
+ $('#error').text('Error: ' + error);
306
+ }
307
+ });
308
+ ```
309
+
310
+ ---
311
+
312
+ ## 📚 Examples
313
+
314
+ ### 1. Basic Usage
315
+
316
+ ```javascript
317
+ $('#mySelect').oneSelect({
318
+ placeholder: 'Select fruits...',
319
+ data: ['Apple', 'Banana', 'Cherry', 'Mango']
320
+ });
321
+ ```
322
+
323
+ ### 2. Object Array
324
+
325
+ ```javascript
326
+ $('#mySelect').oneSelect({
327
+ data: [
328
+ { id: 1, name: 'Apple', category: 'Fruit' },
329
+ { id: 2, name: 'Banana', category: 'Fruit' }
330
+ ],
331
+ valueField: 'id',
332
+ labelField: 'name'
333
+ });
334
+ ```
335
+
336
+ ### 3. Value Parameter (Pre-selected Items)
337
+
338
+ ```javascript
339
+ // Single value
340
+ $('#mySelect').oneSelect({
341
+ data: ['Apple', 'Banana', 'Cherry', 'Mango'],
342
+ value: 'Apple' // 'Apple' will be selected
343
+ });
344
+
345
+ // Array with multiple selection
346
+ $('#mySelect').oneSelect({
347
+ data: ['Apple', 'Banana', 'Cherry', 'Mango'],
348
+ value: ['Apple', 'Cherry'], // Both will be selected
349
+ showBadges: true // Enable badge mode
350
+ });
351
+
352
+ // HTML data attribute
353
+ <div class="one-select"
354
+ data-ones-data='["Apple", "Banana", "Cherry"]'
355
+ data-ones-value='"Apple"'> <!-- Single value -->
356
+ </div>
357
+
358
+ <!-- OR -->
359
+ <div class="one-select"
360
+ data-ones-data='["Apple", "Banana", "Cherry"]'
361
+ data-ones-value='["Apple", "Cherry"]'> <!-- Array -->
362
+ </div>
363
+ ```
364
+
365
+ ### 4. Badge System
366
+
367
+ ```javascript
368
+ // Trigger badges
369
+ $('#mySelect').oneSelect({
370
+ data: ['A', 'B', 'C'],
371
+ showBadges: true
372
+ });
373
+
374
+ // External badges
375
+ <div id="badgesContainer"></div>
376
+
377
+ $('#mySelect').oneSelect({
378
+ data: ['A', 'B', 'C'],
379
+ showBadgesExternal: 'badgesContainer'
380
+ });
381
+ ```
382
+
383
+ ### 5. Form Submission
384
+
385
+ ```javascript
386
+ $('#mySelect').oneSelect({
387
+ data: ['Item 1', 'Item 2'],
388
+ name: 'items',
389
+ multiple: true,
390
+ submitForm: true,
391
+ formId: 'myForm'
392
+ });
393
+ ```
394
+
395
+ Backend example:
396
+ ```javascript
397
+ // Receive items array: ['Item 1', 'Item 2']
398
+ // Process filtered items
399
+ ```
400
+
401
+ ### 6. AJAX Data Loading
402
+
403
+ ```javascript
404
+ $('#mySelect').oneSelect({
405
+ ajax: {
406
+ url: '/api/categories',
407
+ method: 'GET'
408
+ },
409
+ beforeLoad: function() {
410
+ console.log('Loading...');
411
+ },
412
+ afterLoad: function(response) {
413
+ console.log('Loaded:', response);
414
+ }
415
+ });
416
+ ```
417
+
418
+ ### 7. Multiple Instances
419
+
420
+ ```javascript
421
+ // Each has independent ID
422
+ var fruits = $('#fruits').oneSelect({
423
+ data: ['Apple', 'Banana', 'Cherry']
424
+ });
425
+
426
+ var colors = $('#colors').oneSelect({
427
+ data: ['Red', 'Blue', 'Green']
428
+ });
429
+
430
+ // External control over instances
431
+ var fruitsInstance = OneSelect.getInstance(
432
+ $('#fruits').oneSelect('getInstanceId')
433
+ );
434
+ fruitsInstance.selectAll();
435
+ ```
436
+
437
+ ### 8. Search Feature
438
+
439
+ ```javascript
440
+ // Enable local search (filters existing data)
441
+ $('#mySelect').oneSelect({
442
+ data: ['Apple', 'Banana', 'Cherry', 'Mango', 'Orange', 'Grape'],
443
+ showSearch: true,
444
+ searchPlaceholder: 'Type to search...'
445
+ });
446
+
447
+ // Enable AJAX search (with debounce)
448
+ $('#mySelect').oneSelect({
449
+ showSearch: true,
450
+ searchUrl: '/api/search',
451
+ searchDebounceDelay: 500,
452
+ searchPlaceholder: 'Search items...'
453
+ });
454
+
455
+ // HTML data attribute example (local search)
456
+ <div class="one-select"
457
+ data-ones-data='["Apple", "Banana", "Cherry", "Mango"]'
458
+ data-ones-show-search="true"
459
+ data-ones-search-placeholder="Find a fruit...">
460
+ </div>
461
+
462
+ // HTML data attribute example (AJAX search)
463
+ <div class="one-select"
464
+ data-ones-show-search="true"
465
+ data-ones-search-url="/api/customers/search"
466
+ data-ones-search-debounce-delay="300"
467
+ data-ones-search-placeholder="Search customers...">
468
+ </div>
469
+ ```
470
+
471
+ **AJAX Search Server Response Format:**
472
+
473
+ The server should respond with JSON in one of these formats:
474
+
475
+ ```json
476
+ // Direct array
477
+ ["Apple", "Banana", "Cherry"]
478
+
479
+ // Wrapped with 'data'
480
+ {
481
+ "data": [{"value": 1, "label": "Apple"}, {"value": 2, "label": "Banana"}]
482
+ }
483
+
484
+ // Wrapped with 'results'
485
+ {
486
+ "results": ["Apple", "Banana"]
487
+ }
488
+ ```
489
+
490
+ The request will be sent as `GET /api/search?q=searchterm`
491
+
492
+ ---
493
+
494
+ ## 📦 Horizontal Scroll Behavior
495
+
496
+ **Automatic dropdown closing on horizontal scroll:**
497
+
498
+ The dropdown automatically closes when the user performs any horizontal scrolling action while the dropdown is open. This prevents the dropdown from appearing in the wrong position when:
499
+
500
+ - Tables with `overflow-x: auto` are scrolled horizontally
501
+ - Any scrollable container is scrolled horizontally
502
+ - Touchpad/trackpad horizontal gestures are used
503
+ - Mouse wheel horizontal scrolling is performed
504
+
505
+ ### How It Works
506
+
507
+ The plugin uses two methods to detect horizontal scroll:
508
+
509
+ 1. **Wheel Event Detection**: Detects horizontal mouse/touchpad scrolling in real-time
510
+ 2. **Periodic Scroll Checking**: Every 50ms, checks if any scrollable element's `scrollLeft` position has changed
511
+
512
+ When horizontal scroll is detected, the dropdown immediately closes.
513
+
514
+ ### Example
515
+
516
+ ```html
517
+ <div style="overflow-x: auto; width: 100%;">
518
+ <table style="width: 2000px;">
519
+ <tr>
520
+ <td>
521
+ <div id="mySelect"></div>
522
+ </td>
523
+ <td>Other columns...</td>
524
+ </tr>
525
+ </table>
526
+ </div>
527
+
528
+ <script>
529
+ $('#mySelect').oneSelect({
530
+ data: ['Apple', 'Banana', 'Cherry']
531
+ });
532
+ </script>
533
+ ```
534
+
535
+ **Behavior:**
536
+ - User opens dropdown ✅
537
+ - User scrolls table horizontally → **Dropdown closes automatically** ✅
538
+
539
+ ---
540
+
541
+ ## 🔄 AJAX Integration
542
+
543
+ ### AJAX Configuration
544
+
545
+ ```javascript
546
+ $('#mySelect').oneSelect({
547
+ ajax: {
548
+ url: '/api/items',
549
+ method: 'GET',
550
+ data: { category: 'fruits', active: true }
551
+ },
552
+ autoLoad: false
553
+ });
554
+
555
+ // Manual load
556
+ $('#loadBtn').on('click', function() {
557
+ $('#mySelect').oneSelect('loadData', {
558
+ url: '/api/different-items',
559
+ data: { filter: 'active' }
560
+ });
561
+ });
562
+ ```
563
+
564
+ ### Supported Response Formats
565
+
566
+ **1. Direct array:**
567
+ ```json
568
+ ["Apple", "Banana", "Cherry"]
569
+ ```
570
+
571
+ **2. Wrapped with 'data':**
572
+ ```json
573
+ {
574
+ "data": ["Apple", "Banana"]
575
+ }
576
+ ```
577
+
578
+ **3. Wrapped with 'results':**
579
+ ```json
580
+ {
581
+ "results": ["Apple", "Banana"]
582
+ }
583
+ ```
584
+
585
+ **4. Object array:**
586
+ ```json
587
+ [
588
+ {"value": 1, "label": "Apple"},
589
+ {"value": 2, "label": "Banana"}
590
+ ]
591
+ ```
592
+
593
+ ---
594
+
595
+ ## 📝 Form Submission
596
+
597
+ ### Hidden Inputs
598
+
599
+ Component automatically creates `<input type="hidden">` elements:
600
+
601
+ ```javascript
602
+ $('#mySelect').oneSelect({
603
+ name: 'items',
604
+ multiple: true,
605
+ data: ['A', 'B', 'C']
606
+ });
607
+ ```
608
+
609
+ HTML result:
610
+ ```html
611
+ <input type="hidden" name="items[]" value="A">
612
+ <input type="hidden" name="items[]" value="B">
613
+ <input type="hidden" name="items[]" value="C">
614
+ ```
615
+
616
+ ---
617
+
618
+ ## 🏷️ Badge System
619
+
620
+ ### Trigger Badges
621
+
622
+ ```javascript
623
+ $('#mySelect').oneSelect({
624
+ showBadges: true
625
+ });
626
+ ```
627
+
628
+ Result: `[Apple ×] [Banana ×] [Cherry ×]`
629
+
630
+ ### External Badges
631
+
632
+ ```javascript
633
+ <div id="myBadges"></div>
634
+
635
+ $('#mySelect').oneSelect({
636
+ showBadgesExternal: 'myBadges'
637
+ });
638
+ ```
639
+
640
+ ### Badge Properties
641
+
642
+ - **Background:** `#3b82f6` (light blue)
643
+ - **Text color:** `#fff` (white)
644
+ - **Remove button (×):** `#fff` (white)
645
+ - **Hover:** Light gray background
646
+ - **Remove:** Clicking × button unselects item
647
+
648
+ ### Both Together
649
+
650
+ ```javascript
651
+ $('#mySelect').oneSelect({
652
+ showBadges: true, // Badge in trigger
653
+ showBadgesExternal: 'myBadges' // Badge outside as well
654
+ });
655
+ ```
656
+
657
+ ---
658
+
659
+ ## 🎨 CSS Styling
660
+
661
+ Main CSS classes with `cms-` prefix:
662
+
663
+ ```css
664
+ .one-select /* Main container (relative positioned) */
665
+ .cms-wrapper /* Wrapper (relative positioned) */
666
+ .cms-trigger /* Button that opens dropdown */
667
+ .cms-selected-text /* Selected text */
668
+ .cms-dropdown /* Dropdown menu (absolute positioned) */
669
+ .cms-search-wrapper /* Search input wrapper */
670
+ .cms-search-input /* Search input field */
671
+ .cms-options-container /* Options container */
672
+ .cms-options-container.cms-loading /* Loading state for AJAX search */
673
+ .cms-option /* Single option */
674
+ .cms-option.selected /* Selected option */
675
+ .cms-option.select-all /* "Select All" option */
676
+ .cms-badge /* Badge */
677
+ .cms-badge-remove /* Badge × button */
678
+ .cms-btn /* OK/Cancel buttons */
679
+ ```
680
+
681
+ ### DOM Structure
682
+
683
+ ```html
684
+ <!-- Wrapper element (your container) -->
685
+ <div class="one-select">
686
+ <div class="cms-wrapper">
687
+ <div class="cms-trigger">
688
+ <span class="cms-selected-text">Select...</span>
689
+ </div>
690
+ </div>
691
+ </div>
692
+
693
+ <!-- Dropdown is appended to body with dynamic positioning -->
694
+ <div class="cms-dropdown" style="position: fixed; top: ...; left: ...; width: ...;">
695
+ <!-- Search input (when showSearch is true) -->
696
+ <div class="cms-search-wrapper">
697
+ <input type="text" class="cms-search-input" placeholder="Search...">
698
+ </div>
699
+ <div class="cms-options-container">
700
+ <div class="cms-option select-all">...</div>
701
+ <div class="cms-option">...</div>
702
+ </div>
703
+ <div class="cms-buttons">
704
+ <button class="cms-btn cms-btn-ok">OK</button>
705
+ <button class="cms-btn cms-btn-cancel">Cancel</button>
706
+ </div>
707
+ </div>
708
+ ```
709
+
710
+ **Note:** The dropdown is positioned using `position: fixed` with viewport coordinates (`getBoundingClientRect()`). This ensures it stays correctly positioned even when parent elements scroll.
711
+
712
+ ### Custom CSS Example
713
+
714
+ ```css
715
+ /* Selected option styling */
716
+ .cms-option.selected label {
717
+ font-weight: 600;
718
+ color: #007bff;
719
+ }
720
+
721
+ /* Badge styling */
722
+ .cms-badge {
723
+ background: #3b82f6;
724
+ color: #fff;
725
+ border-radius: 12px;
726
+ padding: 3px 8px;
727
+ }
728
+
729
+ /* Search input styling */
730
+ .cms-search-input {
731
+ background: #f8f9fa;
732
+ border-color: #dee2e6;
733
+ }
734
+ .cms-search-input:focus {
735
+ border-color: #3b82f6;
736
+ box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.2);
737
+ }
738
+
739
+ /* Custom dropdown width */
740
+ .one-select {
741
+ width: 400px;
742
+ }
743
+ ```
744
+
745
+ ---
746
+
747
+ ## 🚀 Quick Usage Examples
748
+
749
+ ```javascript
750
+ // 1. Simple
751
+ $('#select1').oneSelect({
752
+ data: ['A', 'B', 'C']
753
+ });
754
+
755
+ // 2. Object array
756
+ $('#select2').oneSelect({
757
+ data: [{id: 1, title: 'A'}, {id: 2, title: 'B'}],
758
+ valueField: 'id',
759
+ labelField: 'title'
760
+ });
761
+
762
+ // 3. Badges
763
+ $('#select3').oneSelect({
764
+ data: ['X', 'Y', 'Z'],
765
+ showBadges: true
766
+ });
767
+
768
+ // 4. Form submission
769
+ $('#select4').oneSelect({
770
+ data: ['P1', 'P2'],
771
+ name: 'products',
772
+ multiple: true,
773
+ submitForm: true,
774
+ formId: 'myForm'
775
+ });
776
+
777
+ // 5. AJAX
778
+ $('#select5').oneSelect({
779
+ ajax: {url: '/api/items'},
780
+ autoLoad: false
781
+ });
782
+
783
+ // 6. Click outside behavior
784
+ $('#select6').oneSelect({
785
+ closeOnOutside: true // Close when clicking outside (default)
786
+ });
787
+
788
+ // 7. Pre-selected items
789
+ $('#select7').oneSelect({
790
+ data: ['Apple', 'Banana', 'Cherry'],
791
+ value: ['Apple', 'Cherry'] // Pre-select these items
792
+ });
793
+
794
+ // 8. Search feature (local filtering)
795
+ $('#select8').oneSelect({
796
+ data: ['Apple', 'Banana', 'Cherry', 'Mango', 'Orange'],
797
+ showSearch: true,
798
+ searchPlaceholder: 'Search fruits...'
799
+ });
800
+
801
+ // 9. AJAX search with debounce
802
+ $('#select9').oneSelect({
803
+ showSearch: true,
804
+ searchUrl: '/api/search',
805
+ searchDebounceDelay: 500
806
+ });
807
+ ```
808
+
809
+ ---
810
+
811
+ ## 📞 Support
812
+
813
+ **OneSelect** - jQuery Multi-Select Dropdown Component
814
+
815
+ ### Project Structure
816
+
817
+ ```
818
+ one-select/
819
+ ├── css/
820
+ │ └── one-select.min.css
821
+ ├── js/
822
+ │ └── one-select.js
823
+ └── README.md
824
+ ```
825
+
826
+ ### File Locations
827
+
828
+ - **JavaScript:** `one-select/js/one-select.js`
829
+ - **CSS:** `one-select/css/one-select.min.css`
830
+ - **Documentation:** `one-select/README.md`
831
+
832
+ ### License
833
+
834
+ MIT License - Feel free to use in your projects!
835
+
836
+ ---
837
+
838
+ ## 🎯 Browser Support
839
+
840
+ - Chrome (latest)
841
+ - Firefox (latest)
842
+ - Safari (latest)
843
+ - Edge (latest)
844
+ - Opera (latest)
845
+
846
+ ---
847
+
848
+ **OneSelect** makes multi-select dropdowns simple and powerful! 🚀