tail-select-rails 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/Readme.md +42 -0
- data/lib/tail_select_rails.rb +6 -0
- data/vendor/assets/javascripts/tail-select-rails.js +603 -0
- data/vendor/assets/javascripts/tail-select-rails.min.js +11 -0
- data/vendor/assets/stylesheets/tail-select-rails.css +254 -0
- metadata +104 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: bb5f0cae3224dd89b8818fd901f9f9d55bbf5f496f27e179b217416bb492fcc4
|
4
|
+
data.tar.gz: c5eb92f904c0113c43aa619996216907f4928a44d5fab1b453f255fd6ab8cda8
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 709ce7b81c61db1aae91b4cf86beca85f76e07b15f37928eeb7088ea44a1e60b061239d9b3255062fdc99dc074a49d421a86b3f703f8d88ea67fb3124843bcc7
|
7
|
+
data.tar.gz: fca58fd169a1ddc2fa8b86f9f8ad2954ff1d08ffa84e934be22d8eff2e3d240df72f95d46ce29dda4e7304b9693464a0f3e6347cfda10752e1785b9d4af75409
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2024 Gajendra Jena <gaju.mca@gmail.com>
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge as the original js library is opensource
|
6
|
+
, to any person obtaining a copy of this software and associated documentation files
|
7
|
+
(the "Software"), to deal in the Software without restriction, including without
|
8
|
+
limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
9
|
+
and/or sell copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/Readme.md
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
tail-select-rails
|
2
|
+
=================
|
3
|
+
|
4
|
+
A gem to easily integrate [tail-select](https://getbutterfly.com/tail-select/) with the rails 6.1+ asset pipeline.
|
5
|
+
|
6
|
+
Currently this gem brings support for tail-select **v1.0.2**.
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
|
10
|
+
First add the following lines to your applications `Gemfile`:
|
11
|
+
|
12
|
+
``` ruby
|
13
|
+
gem 'tail-select-rails'
|
14
|
+
```
|
15
|
+
|
16
|
+
Run `bundle install`.
|
17
|
+
|
18
|
+
Add the following lines to `app/assets/javascripts/application.js`:
|
19
|
+
|
20
|
+
``` javascript
|
21
|
+
//= require jquery
|
22
|
+
//= require tail-select-rails
|
23
|
+
```
|
24
|
+
|
25
|
+
Finalize the setup by adding these lines to `app/assets/stylesheets/application.css`:
|
26
|
+
|
27
|
+
``` css
|
28
|
+
/*
|
29
|
+
*= require tail-select-rails
|
30
|
+
*/
|
31
|
+
```
|
32
|
+
|
33
|
+
## Usage
|
34
|
+
|
35
|
+
Please refer to [https://getbutterfly.com/tail-select/](https://getbutterfly.com/tail-select/)
|
36
|
+
for all the details on how to use.
|
37
|
+
|
38
|
+
### Credits
|
39
|
+
|
40
|
+
* tail-select-rails is (c) Copyright Gajendra Jena
|
41
|
+
* This gem is maintained by [@gajendrajena](https://www.twitter.com/gajendrajena)
|
42
|
+
|
@@ -0,0 +1,603 @@
|
|
1
|
+
/**
|
2
|
+
* tail.select - The vanilla JavaScript solution to make your <select> fields awesome!
|
3
|
+
*
|
4
|
+
* @author Ciprian Popescu <getbutterfly@gmail.com>
|
5
|
+
* @version 1.0.2
|
6
|
+
* @url https://getbutterfly.com/tail-select/
|
7
|
+
* @github https://github.com/wolffe/tail.select.js
|
8
|
+
* @license MIT License
|
9
|
+
* @copyright Copyright 2020 - 2024 Ciprian Popescu <getbutterfly@gmail.com>
|
10
|
+
*/
|
11
|
+
const tail = {
|
12
|
+
select: function (selector, options = {}) {
|
13
|
+
// Default options
|
14
|
+
const defaultOptions = {
|
15
|
+
multiTags: false,
|
16
|
+
multiCounter: true,
|
17
|
+
theme: 'light', // light|dark
|
18
|
+
classNames: 'tail-default',
|
19
|
+
strings: {
|
20
|
+
all: "All",
|
21
|
+
none: "None",
|
22
|
+
placeholder: "Select an option...",
|
23
|
+
search: "Type in to search...",
|
24
|
+
}
|
25
|
+
};
|
26
|
+
|
27
|
+
// Merge default options with provided options
|
28
|
+
const opts = { ...defaultOptions, ...options };
|
29
|
+
|
30
|
+
// Extract options
|
31
|
+
const { multiTags, multiCounter, theme, classNames, strings } = opts;
|
32
|
+
|
33
|
+
//
|
34
|
+
const originalSelects = document.querySelectorAll(selector);
|
35
|
+
|
36
|
+
originalSelects.forEach((originalSelect) => {
|
37
|
+
// Hide original dropdown
|
38
|
+
originalSelect.style.display = "none";
|
39
|
+
|
40
|
+
// Create custom dropdown container
|
41
|
+
const customDropdown = document.createElement("div");
|
42
|
+
customDropdown.classList.add("tail-select");
|
43
|
+
customDropdown.classList.add(originalSelect.id);
|
44
|
+
customDropdown.classList.add(opts.classNames);
|
45
|
+
customDropdown.dataset.theme = `tail-theme--${opts.theme}`;
|
46
|
+
|
47
|
+
if (originalSelect.multiple) {
|
48
|
+
customDropdown.classList.add("tail--multiple");
|
49
|
+
} else {
|
50
|
+
customDropdown.classList.add("tail--single");
|
51
|
+
}
|
52
|
+
|
53
|
+
// Create search input
|
54
|
+
const searchInput = document.createElement("input");
|
55
|
+
searchInput.type = "text";
|
56
|
+
searchInput.classList.add('tail--search');
|
57
|
+
searchInput.placeholder = strings.placeholder || "Select an option...";
|
58
|
+
|
59
|
+
// Add focus event to change the placeholder
|
60
|
+
searchInput.addEventListener("focus", () => {
|
61
|
+
searchInput.placeholder = strings.search || "Type in to search...";
|
62
|
+
});
|
63
|
+
|
64
|
+
// Add blur event to revert the placeholder when not focused
|
65
|
+
searchInput.addEventListener("blur", () => {
|
66
|
+
searchInput.placeholder = strings.placeholder || "Select an option...";
|
67
|
+
});
|
68
|
+
|
69
|
+
// Add input event to change the placeholder
|
70
|
+
searchInput.addEventListener("input", () =>
|
71
|
+
filterOptions(originalSelect, searchInput)
|
72
|
+
);
|
73
|
+
|
74
|
+
// Create floating toolbar
|
75
|
+
const tailFloatingToolbar = document.createElement("div");
|
76
|
+
tailFloatingToolbar.classList.add("tail--toolbar");
|
77
|
+
|
78
|
+
// Create toggle-all checkbox
|
79
|
+
const toggleAllCheckbox = document.createElement("input");
|
80
|
+
toggleAllCheckbox.type = "checkbox";
|
81
|
+
toggleAllCheckbox.value = strings.all || "All";
|
82
|
+
toggleAllCheckbox.addEventListener("change", () =>
|
83
|
+
toggleAll(originalSelect, toggleAllCheckbox)
|
84
|
+
);
|
85
|
+
|
86
|
+
const toggleAllLabel = document.createElement("label");
|
87
|
+
toggleAllLabel.textContent = strings.all || "All";
|
88
|
+
toggleAllLabel.classList.add("all");
|
89
|
+
toggleAllLabel.appendChild(toggleAllCheckbox);
|
90
|
+
|
91
|
+
// Create uncheck-all button
|
92
|
+
const uncheckAllButton = document.createElement("button");
|
93
|
+
uncheckAllButton.type = 'button';
|
94
|
+
uncheckAllButton.textContent = strings.none || "None";
|
95
|
+
uncheckAllButton.classList.add("uncheck");
|
96
|
+
uncheckAllButton.addEventListener("click", () =>
|
97
|
+
uncheckAll(originalSelect)
|
98
|
+
);
|
99
|
+
|
100
|
+
if (opts.multiCounter) {
|
101
|
+
// Create counter
|
102
|
+
const counter = document.createElement("span");
|
103
|
+
counter.textContent = "0";
|
104
|
+
counter.classList.add("tail--counter");
|
105
|
+
|
106
|
+
customDropdown.appendChild(counter);
|
107
|
+
}
|
108
|
+
|
109
|
+
// Create nested list
|
110
|
+
const nestedList = document.createElement("div");
|
111
|
+
nestedList.classList.add("tail--nested-dropdown");
|
112
|
+
nestedList.style.display = "none"; // Initially hide the list
|
113
|
+
|
114
|
+
customDropdown.appendChild(searchInput);
|
115
|
+
customDropdown.appendChild(tailFloatingToolbar);
|
116
|
+
customDropdown.appendChild(nestedList);
|
117
|
+
|
118
|
+
tailFloatingToolbar.appendChild(toggleAllLabel);
|
119
|
+
tailFloatingToolbar.appendChild(uncheckAllButton);
|
120
|
+
|
121
|
+
// Insert custom dropdown after the original select
|
122
|
+
originalSelect.insertAdjacentElement("afterend", customDropdown);
|
123
|
+
|
124
|
+
// Create ul element for displaying selected options as pills
|
125
|
+
const selectedOptionsList = document.createElement("ul");
|
126
|
+
selectedOptionsList.classList.add("tail--selected-options-list");
|
127
|
+
|
128
|
+
if (opts.multiTags) {
|
129
|
+
if (originalSelect.multiple) {
|
130
|
+
// Insert selectedOptionsList as the next sibling of customDropdown
|
131
|
+
customDropdown.insertAdjacentElement(
|
132
|
+
"afterend",
|
133
|
+
selectedOptionsList
|
134
|
+
);
|
135
|
+
}
|
136
|
+
}
|
137
|
+
//
|
138
|
+
|
139
|
+
function buildNestedList() {
|
140
|
+
const fragment = document.createDocumentFragment();
|
141
|
+
|
142
|
+
const optgroups = originalSelect.getElementsByTagName(
|
143
|
+
"optgroup"
|
144
|
+
);
|
145
|
+
|
146
|
+
if (optgroups.length > 0) {
|
147
|
+
for (let i = 0; i < optgroups.length; i++) {
|
148
|
+
const optgroup = optgroups[i];
|
149
|
+
const optgroupItem = document.createElement("div");
|
150
|
+
optgroupItem.classList.add("tail--optgroup");
|
151
|
+
|
152
|
+
// Create label for optgroup
|
153
|
+
const optgroupLabel = document.createElement("label");
|
154
|
+
|
155
|
+
// Create checkbox for optgroup
|
156
|
+
const optgroupCheckbox = document.createElement(
|
157
|
+
"input"
|
158
|
+
);
|
159
|
+
optgroupCheckbox.type = "checkbox";
|
160
|
+
optgroupCheckbox.value = optgroup.label;
|
161
|
+
optgroupCheckbox.addEventListener("change", () =>
|
162
|
+
toggleOptgroup(optgroupCheckbox)
|
163
|
+
);
|
164
|
+
optgroupLabel.appendChild(optgroupCheckbox);
|
165
|
+
|
166
|
+
// Label text for optgroup
|
167
|
+
const optgroupLabelText = document.createElement(
|
168
|
+
"span"
|
169
|
+
);
|
170
|
+
optgroupLabelText.textContent = optgroup.label;
|
171
|
+
optgroupLabelText.classList.add("tail--optgroup-label");
|
172
|
+
optgroupLabel.appendChild(optgroupLabelText);
|
173
|
+
|
174
|
+
optgroupItem.appendChild(optgroupLabel);
|
175
|
+
|
176
|
+
// Nested options list
|
177
|
+
const nestedOptionsList = document.createElement("div");
|
178
|
+
|
179
|
+
// Add ARIA attributes to the nested options list
|
180
|
+
nestedOptionsList.setAttribute("role", "listbox");
|
181
|
+
nestedOptionsList.classList.add("tail--nested-dropdown-list");
|
182
|
+
const options = optgroup.getElementsByTagName("option");
|
183
|
+
|
184
|
+
for (let j = 0; j < options.length; j++) {
|
185
|
+
const option = options[j];
|
186
|
+
const optionItem = document.createElement("div");
|
187
|
+
optionItem.classList.add("tail--nested-dropdown-item");
|
188
|
+
|
189
|
+
// Create checkbox for option
|
190
|
+
const optionCheckbox = document.createElement(
|
191
|
+
"input"
|
192
|
+
);
|
193
|
+
optionCheckbox.type = "checkbox";
|
194
|
+
optionCheckbox.value = option.textContent;
|
195
|
+
|
196
|
+
// Create label for option
|
197
|
+
const optionLabel = document.createElement("label");
|
198
|
+
|
199
|
+
// Label for option text
|
200
|
+
const optionLabelText = document.createElement(
|
201
|
+
"span"
|
202
|
+
);
|
203
|
+
optionLabelText.textContent = option.textContent;
|
204
|
+
|
205
|
+
// Option description
|
206
|
+
if (option.dataset.description) {
|
207
|
+
optionLabelText.innerHTML += `<small>${option.dataset.description}</small>`;
|
208
|
+
}
|
209
|
+
|
210
|
+
// Check it
|
211
|
+
if (option.selected && option.hasAttribute('selected')) {
|
212
|
+
optionCheckbox.checked = true;
|
213
|
+
updateCounter(originalSelect);
|
214
|
+
updateCustomTextInput(originalSelect);
|
215
|
+
}
|
216
|
+
|
217
|
+
//
|
218
|
+
|
219
|
+
optionLabel.appendChild(optionCheckbox);
|
220
|
+
optionLabel.appendChild(optionLabelText);
|
221
|
+
|
222
|
+
optionItem.appendChild(optionLabel);
|
223
|
+
|
224
|
+
nestedOptionsList.appendChild(optionItem);
|
225
|
+
}
|
226
|
+
|
227
|
+
optgroupItem.appendChild(nestedOptionsList);
|
228
|
+
nestedList.appendChild(optgroupItem);
|
229
|
+
}
|
230
|
+
} else {
|
231
|
+
const options = originalSelect.getElementsByTagName(
|
232
|
+
"option"
|
233
|
+
);
|
234
|
+
|
235
|
+
for (let j = 0; j < options.length; j++) {
|
236
|
+
const option = options[j];
|
237
|
+
const optionItem = document.createElement("div");
|
238
|
+
optionItem.classList.add("tail--nested-dropdown-item");
|
239
|
+
|
240
|
+
// Create checkbox for option
|
241
|
+
const optionCheckbox = document.createElement("input");
|
242
|
+
optionCheckbox.type = "checkbox";
|
243
|
+
optionCheckbox.value = option.textContent;
|
244
|
+
|
245
|
+
// Create label for option
|
246
|
+
const optionLabel = document.createElement("label");
|
247
|
+
|
248
|
+
// Label for option text
|
249
|
+
const optionLabelText = document.createElement("span");
|
250
|
+
optionLabelText.textContent = option.textContent;
|
251
|
+
|
252
|
+
// Option description
|
253
|
+
if (option.dataset.description) {
|
254
|
+
optionLabelText.innerHTML += `<small>${option.dataset.description}</small>`;
|
255
|
+
}
|
256
|
+
|
257
|
+
// Check it
|
258
|
+
if (option.selected && option.hasAttribute('selected')) {
|
259
|
+
optionCheckbox.checked = true;
|
260
|
+
updateCounter(originalSelect);
|
261
|
+
updateCustomTextInput(originalSelect);
|
262
|
+
}
|
263
|
+
//
|
264
|
+
|
265
|
+
optionLabel.appendChild(optionCheckbox);
|
266
|
+
optionLabel.appendChild(optionLabelText);
|
267
|
+
|
268
|
+
optionItem.appendChild(optionLabel);
|
269
|
+
|
270
|
+
nestedList.appendChild(optionItem);
|
271
|
+
}
|
272
|
+
}
|
273
|
+
|
274
|
+
// Add ARIA attributes to the custom dropdown container
|
275
|
+
customDropdown.setAttribute("role", "combobox");
|
276
|
+
customDropdown.setAttribute("aria-haspopup", "true");
|
277
|
+
customDropdown.setAttribute("aria-expanded", "false");
|
278
|
+
|
279
|
+
attachOptionCheckboxListeners();
|
280
|
+
|
281
|
+
// Append the fragment to the DOM once all changes are made
|
282
|
+
nestedList.appendChild(fragment);
|
283
|
+
}
|
284
|
+
|
285
|
+
function toggleAll(originalSelect, toggleAllCheckbox) {
|
286
|
+
const isChecked = toggleAllCheckbox.checked;
|
287
|
+
const optionCheckboxes = nestedList.querySelectorAll(
|
288
|
+
'input[type="checkbox"]'
|
289
|
+
);
|
290
|
+
|
291
|
+
optionCheckboxes.forEach((checkbox) => {
|
292
|
+
checkbox.checked = isChecked;
|
293
|
+
updateOriginalOptionState(originalSelect, checkbox);
|
294
|
+
});
|
295
|
+
}
|
296
|
+
|
297
|
+
function uncheckAll(originalSelect) {
|
298
|
+
const optionCheckboxes = nestedList.querySelectorAll(
|
299
|
+
'input[type="checkbox"]'
|
300
|
+
);
|
301
|
+
|
302
|
+
optionCheckboxes.forEach((checkbox) => {
|
303
|
+
checkbox.checked = false;
|
304
|
+
updateOriginalOptionState(originalSelect, checkbox);
|
305
|
+
});
|
306
|
+
|
307
|
+
// Uncheck the original <select> options
|
308
|
+
const originalOptions = originalSelect.getElementsByTagName(
|
309
|
+
"option"
|
310
|
+
);
|
311
|
+
for (let i = 0; i < originalOptions.length; i++) {
|
312
|
+
originalOptions[i].selected = false;
|
313
|
+
}
|
314
|
+
}
|
315
|
+
|
316
|
+
function toggleOption(checkbox) {
|
317
|
+
if (originalSelect.multiple) {
|
318
|
+
updateOriginalOptionState(originalSelect, checkbox);
|
319
|
+
} else {
|
320
|
+
// For single-select, uncheck all and check the current one
|
321
|
+
const optionCheckboxes = nestedList.querySelectorAll(
|
322
|
+
'.tail--nested-dropdown-item input[type="checkbox"]'
|
323
|
+
);
|
324
|
+
optionCheckboxes.forEach((cb) => (cb.checked = false));
|
325
|
+
checkbox.checked = true;
|
326
|
+
updateOriginalOptionState(originalSelect, checkbox);
|
327
|
+
}
|
328
|
+
}
|
329
|
+
|
330
|
+
function toggleOptgroup(optgroupCheckbox) {
|
331
|
+
const isChecked = optgroupCheckbox.checked;
|
332
|
+
const nestedOptionsList = optgroupCheckbox
|
333
|
+
.closest(".tail--optgroup")
|
334
|
+
.querySelector(".tail--nested-dropdown-list");
|
335
|
+
const optionCheckboxes = nestedOptionsList.querySelectorAll(
|
336
|
+
'input[type="checkbox"]'
|
337
|
+
);
|
338
|
+
|
339
|
+
optionCheckboxes.forEach((checkbox) => {
|
340
|
+
checkbox.checked = isChecked;
|
341
|
+
toggleOption(checkbox); // Call toggleOption for individual options
|
342
|
+
});
|
343
|
+
|
344
|
+
if (!originalSelect.multiple) {
|
345
|
+
// For single-select, uncheck all other checkboxes in the same optgroup
|
346
|
+
const customDropdown = originalSelect.closest(
|
347
|
+
".tail-select"
|
348
|
+
);
|
349
|
+
if (customDropdown) {
|
350
|
+
const otherOptgroupCheckboxes = customDropdown.querySelectorAll(
|
351
|
+
'.tail--nested-dropdown-item input[type="checkbox"]'
|
352
|
+
);
|
353
|
+
|
354
|
+
otherOptgroupCheckboxes.forEach((cb) => {
|
355
|
+
if (cb !== optgroupCheckbox) {
|
356
|
+
cb.checked = false;
|
357
|
+
updateOriginalOptionState(originalSelect, cb);
|
358
|
+
}
|
359
|
+
});
|
360
|
+
}
|
361
|
+
}
|
362
|
+
|
363
|
+
updateOriginalOptionState(originalSelect, optgroupCheckbox);
|
364
|
+
}
|
365
|
+
|
366
|
+
function attachOptionCheckboxListeners() {
|
367
|
+
const optionCheckboxes = nestedList.querySelectorAll(
|
368
|
+
'.tail--nested-dropdown-item input[type="checkbox"]'
|
369
|
+
);
|
370
|
+
|
371
|
+
optionCheckboxes.forEach((checkbox) => {
|
372
|
+
checkbox.addEventListener("change", () =>
|
373
|
+
toggleOption(checkbox)
|
374
|
+
);
|
375
|
+
});
|
376
|
+
}
|
377
|
+
|
378
|
+
function updateOriginalOptionState(
|
379
|
+
originalSelect,
|
380
|
+
checkbox,
|
381
|
+
customDropdown
|
382
|
+
) {
|
383
|
+
const optionValue = checkbox.value;
|
384
|
+
const option = Array.from(originalSelect.options).find(
|
385
|
+
(opt) =>
|
386
|
+
opt.value === optionValue ||
|
387
|
+
opt.textContent === optionValue
|
388
|
+
);
|
389
|
+
|
390
|
+
if (option) {
|
391
|
+
if (checkbox.checked) {
|
392
|
+
option.selected = true;
|
393
|
+
} else {
|
394
|
+
option.selected = false;
|
395
|
+
}
|
396
|
+
|
397
|
+
// Trigger change event for the original select
|
398
|
+
const event = new Event("change", { bubbles: true });
|
399
|
+
originalSelect.dispatchEvent(event);
|
400
|
+
}
|
401
|
+
|
402
|
+
// Get all selected options
|
403
|
+
const selectedOptions = Array.from(
|
404
|
+
originalSelect.options
|
405
|
+
).filter((opt) => opt.selected);
|
406
|
+
|
407
|
+
// Update the search input value with the selected option text
|
408
|
+
if (!originalSelect.multiple) {
|
409
|
+
if (selectedOptions.length > 0 && searchInput) {
|
410
|
+
searchInput.value = selectedOptions[0].textContent;
|
411
|
+
} else {
|
412
|
+
searchInput.value = ""; // Clear the search input if no option is selected
|
413
|
+
}
|
414
|
+
} else {
|
415
|
+
// Update searchInput value with selected options
|
416
|
+
searchInput.value = selectedOptions
|
417
|
+
.map((opt) => opt.textContent)
|
418
|
+
.join(", ");
|
419
|
+
}
|
420
|
+
|
421
|
+
if (opts.multiTags) {
|
422
|
+
if (originalSelect.multiple) {
|
423
|
+
// Update the selected options list
|
424
|
+
updateSelectedOptionsList(
|
425
|
+
selectedOptionsList,
|
426
|
+
selectedOptions
|
427
|
+
);
|
428
|
+
}
|
429
|
+
}
|
430
|
+
|
431
|
+
// Convert selected options to an array of values
|
432
|
+
const selectedValues = selectedOptions.map((opt) => opt.value);
|
433
|
+
// Log the selected values to the console
|
434
|
+
// console.log(selectedValues);
|
435
|
+
|
436
|
+
var options = originalSelect.options,
|
437
|
+
count = 0;
|
438
|
+
for (var i = 0; i < options.length; i++) {
|
439
|
+
if (options[i].selected) count++;
|
440
|
+
}
|
441
|
+
|
442
|
+
if (opts.multiCounter) {
|
443
|
+
// Update the counter element
|
444
|
+
let customId = originalSelect.id;
|
445
|
+
if (customId) {
|
446
|
+
let counterElement = document
|
447
|
+
.querySelector(`.${customId}`)
|
448
|
+
.querySelector(".tail--counter");
|
449
|
+
|
450
|
+
if (counterElement) {
|
451
|
+
counterElement.textContent = count;
|
452
|
+
}
|
453
|
+
}
|
454
|
+
}
|
455
|
+
}
|
456
|
+
|
457
|
+
function filterOptions(originalSelect, searchInput) {
|
458
|
+
const searchTerm = searchInput.value.trim().toLowerCase();
|
459
|
+
const optionItems = nestedList.querySelectorAll("div");
|
460
|
+
|
461
|
+
optionItems.forEach((optionItem) => {
|
462
|
+
const optionCheckbox = optionItem.querySelector(
|
463
|
+
'input[type="checkbox"]'
|
464
|
+
);
|
465
|
+
const optionLabel = optionCheckbox.nextElementSibling.textContent.toLowerCase();
|
466
|
+
const optgroupItem = optionItem.closest("div");
|
467
|
+
|
468
|
+
// Hide or show options based on the search term
|
469
|
+
optionCheckbox.style.display = optionLabel.includes(
|
470
|
+
searchTerm
|
471
|
+
)
|
472
|
+
? "inline-block"
|
473
|
+
: "none";
|
474
|
+
});
|
475
|
+
|
476
|
+
optionItems.forEach((optionItem) => {
|
477
|
+
const optionCheckbox = optionItem.querySelector(
|
478
|
+
'input[type="checkbox"]'
|
479
|
+
);
|
480
|
+
const optgroupItem = optionItem.closest("div");
|
481
|
+
|
482
|
+
// Check if there are visible checkboxes within the nested list
|
483
|
+
const nestedCheckboxes = optionItem.querySelectorAll(
|
484
|
+
'div input[type="checkbox"]:not([style="display: none;"])'
|
485
|
+
);
|
486
|
+
const hasVisibleNestedCheckboxes =
|
487
|
+
nestedCheckboxes.length > 0;
|
488
|
+
|
489
|
+
// Show the parent li only if the checkbox or a visible nested checkbox is present
|
490
|
+
optgroupItem.style.display =
|
491
|
+
optionCheckbox.style.display === "inline-block" ||
|
492
|
+
hasVisibleNestedCheckboxes
|
493
|
+
? "block"
|
494
|
+
: "none";
|
495
|
+
});
|
496
|
+
}
|
497
|
+
|
498
|
+
function updateSelectedOptionsList(
|
499
|
+
selectedOptionsList,
|
500
|
+
selectedOptions
|
501
|
+
) {
|
502
|
+
// Clear existing list items
|
503
|
+
selectedOptionsList.innerHTML = "";
|
504
|
+
|
505
|
+
// Create list items for each selected option
|
506
|
+
selectedOptions.forEach((opt) => {
|
507
|
+
const listItem = document.createElement("li");
|
508
|
+
listItem.textContent = opt.textContent;
|
509
|
+
selectedOptionsList.appendChild(listItem);
|
510
|
+
});
|
511
|
+
}
|
512
|
+
|
513
|
+
function updateCustomTextInput(originalSelect) {
|
514
|
+
// Get all selected options
|
515
|
+
const selectedOptions = Array.from(originalSelect.options).filter((opt) => {
|
516
|
+
// Check if the option is selected and has the 'selected' attribute
|
517
|
+
return opt.selected && opt.hasAttribute('selected');
|
518
|
+
});
|
519
|
+
|
520
|
+
|
521
|
+
// Update the search input value with the selected option text
|
522
|
+
if (!originalSelect.multiple) {
|
523
|
+
if (selectedOptions.length > 0 && searchInput) {
|
524
|
+
searchInput.value = selectedOptions[0].textContent;
|
525
|
+
} else {
|
526
|
+
searchInput.value = ""; // Clear the search input if no option is selected
|
527
|
+
}
|
528
|
+
} else {
|
529
|
+
// Update searchInput value with selected options
|
530
|
+
searchInput.value = selectedOptions
|
531
|
+
.map((opt) => opt.textContent)
|
532
|
+
.join(", ");
|
533
|
+
}
|
534
|
+
|
535
|
+
if (opts.multiTags) {
|
536
|
+
if (originalSelect.multiple) {
|
537
|
+
// Update the selected options list
|
538
|
+
updateSelectedOptionsList(
|
539
|
+
selectedOptionsList,
|
540
|
+
selectedOptions
|
541
|
+
);
|
542
|
+
}
|
543
|
+
}
|
544
|
+
}
|
545
|
+
|
546
|
+
|
547
|
+
function updateCounter(originalSelect) {
|
548
|
+
// Get the custom ID for the current original select
|
549
|
+
let customId = originalSelect.id;
|
550
|
+
|
551
|
+
if (customId) {
|
552
|
+
// Get the counter element
|
553
|
+
let counterElement = document
|
554
|
+
.querySelector(`.${customId}`)
|
555
|
+
.querySelector(".tail--counter");
|
556
|
+
|
557
|
+
if (counterElement) {
|
558
|
+
// Get the count of selected options
|
559
|
+
const count = Array.from(originalSelect.options).filter(
|
560
|
+
(opt) => opt.selected
|
561
|
+
).length;
|
562
|
+
|
563
|
+
// Update the counter element
|
564
|
+
counterElement.textContent = count;
|
565
|
+
}
|
566
|
+
}
|
567
|
+
}
|
568
|
+
|
569
|
+
function toggleDropdownVisibility() {
|
570
|
+
nestedList.style.display = "block";
|
571
|
+
customDropdown.setAttribute("aria-expanded", "true");
|
572
|
+
}
|
573
|
+
|
574
|
+
function hideDropdown() {
|
575
|
+
nestedList.style.display = "none";
|
576
|
+
customDropdown.setAttribute("aria-expanded", "false");
|
577
|
+
}
|
578
|
+
|
579
|
+
function handleClickOutside(event) {
|
580
|
+
if (!customDropdown.contains(event.target)) {
|
581
|
+
hideDropdown();
|
582
|
+
}
|
583
|
+
}
|
584
|
+
|
585
|
+
function handleKeyDown(event) {
|
586
|
+
if (event.key === "Escape") {
|
587
|
+
hideDropdown();
|
588
|
+
}
|
589
|
+
}
|
590
|
+
|
591
|
+
// Show the dropdown when the input field is focused
|
592
|
+
searchInput.addEventListener("focus", toggleDropdownVisibility);
|
593
|
+
|
594
|
+
// Hide the dropdown when clicking outside of it
|
595
|
+
document.addEventListener("click", handleClickOutside);
|
596
|
+
|
597
|
+
// Hide the dropdown when pressing the ESC key
|
598
|
+
document.addEventListener("keydown", handleKeyDown);
|
599
|
+
|
600
|
+
buildNestedList();
|
601
|
+
});
|
602
|
+
}
|
603
|
+
};
|
@@ -0,0 +1,11 @@
|
|
1
|
+
/**
|
2
|
+
* tail.select - The vanilla JavaScript solution to make your <select> fields awesome!
|
3
|
+
*
|
4
|
+
* @author Ciprian Popescu <getbutterfly@gmail.com>
|
5
|
+
* @version 1.0.2
|
6
|
+
* @url https://getbutterfly.com/tail-select/
|
7
|
+
* @github https://github.com/wolffe/tail.select.js
|
8
|
+
* @license MIT License
|
9
|
+
* @copyright Copyright 2020 - 2024 Ciprian Popescu <getbutterfly@gmail.com>
|
10
|
+
*/
|
11
|
+
const tail={select:function(selector,options={}){const defaultOptions={multiTags:!1,multiCounter:!0,theme:"light",classNames:"tail-default",strings:{all:"All",none:"None",placeholder:"Select an option...",search:"Type in to search..."}},opts={...defaultOptions,...options},{multiTags:multiTags,multiCounter:multiCounter,theme:theme,classNames:classNames,strings:strings}=opts,originalSelects=document.querySelectorAll(selector);originalSelects.forEach(originalSelect=>{originalSelect.style.display="none";const customDropdown=document.createElement("div");customDropdown.classList.add("tail-select"),customDropdown.classList.add(originalSelect.id),customDropdown.classList.add(opts.classNames),customDropdown.dataset.theme=`tail-theme--${opts.theme}`,originalSelect.multiple?customDropdown.classList.add("tail--multiple"):customDropdown.classList.add("tail--single");const searchInput=document.createElement("input");searchInput.type="text",searchInput.classList.add("tail--search"),searchInput.placeholder=strings.placeholder||"Select an option...",searchInput.addEventListener("focus",()=>{searchInput.placeholder=strings.search||"Type in to search..."}),searchInput.addEventListener("blur",()=>{searchInput.placeholder=strings.placeholder||"Select an option..."}),searchInput.addEventListener("input",()=>filterOptions(originalSelect,searchInput));const tailFloatingToolbar=document.createElement("div");tailFloatingToolbar.classList.add("tail--toolbar");const toggleAllCheckbox=document.createElement("input");toggleAllCheckbox.type="checkbox",toggleAllCheckbox.value=strings.all||"All",toggleAllCheckbox.addEventListener("change",()=>toggleAll(originalSelect,toggleAllCheckbox));const toggleAllLabel=document.createElement("label");toggleAllLabel.textContent=strings.all||"All",toggleAllLabel.classList.add("all"),toggleAllLabel.appendChild(toggleAllCheckbox);const uncheckAllButton=document.createElement("button");if(uncheckAllButton.type="button",uncheckAllButton.textContent=strings.none||"None",uncheckAllButton.classList.add("uncheck"),uncheckAllButton.addEventListener("click",()=>uncheckAll(originalSelect)),opts.multiCounter){const counter=document.createElement("span");counter.textContent="0",counter.classList.add("tail--counter"),customDropdown.appendChild(counter)}const nestedList=document.createElement("div");nestedList.classList.add("tail--nested-dropdown"),nestedList.style.display="none",customDropdown.appendChild(searchInput),customDropdown.appendChild(tailFloatingToolbar),customDropdown.appendChild(nestedList),tailFloatingToolbar.appendChild(toggleAllLabel),tailFloatingToolbar.appendChild(uncheckAllButton),originalSelect.insertAdjacentElement("afterend",customDropdown);const selectedOptionsList=document.createElement("ul");function buildNestedList(){const fragment=document.createDocumentFragment(),optgroups=originalSelect.getElementsByTagName("optgroup");if(optgroups.length>0)for(let i=0;i<optgroups.length;i++){const optgroup=optgroups[i],optgroupItem=document.createElement("div");optgroupItem.classList.add("tail--optgroup");const optgroupLabel=document.createElement("label"),optgroupCheckbox=document.createElement("input");optgroupCheckbox.type="checkbox",optgroupCheckbox.value=optgroup.label,optgroupCheckbox.addEventListener("change",()=>toggleOptgroup(optgroupCheckbox)),optgroupLabel.appendChild(optgroupCheckbox);const optgroupLabelText=document.createElement("span");optgroupLabelText.textContent=optgroup.label,optgroupLabelText.classList.add("tail--optgroup-label"),optgroupLabel.appendChild(optgroupLabelText),optgroupItem.appendChild(optgroupLabel);const nestedOptionsList=document.createElement("div");nestedOptionsList.setAttribute("role","listbox"),nestedOptionsList.classList.add("tail--nested-dropdown-list");const options=optgroup.getElementsByTagName("option");for(let j=0;j<options.length;j++){const option=options[j],optionItem=document.createElement("div");optionItem.classList.add("tail--nested-dropdown-item");const optionCheckbox=document.createElement("input");optionCheckbox.type="checkbox",optionCheckbox.value=option.textContent;const optionLabel=document.createElement("label"),optionLabelText=document.createElement("span");optionLabelText.textContent=option.textContent,option.dataset.description&&(optionLabelText.innerHTML+=`<small>${option.dataset.description}</small>`),option.selected&&option.hasAttribute("selected")&&(optionCheckbox.checked=!0,updateCounter(originalSelect),updateCustomTextInput(originalSelect)),optionLabel.appendChild(optionCheckbox),optionLabel.appendChild(optionLabelText),optionItem.appendChild(optionLabel),nestedOptionsList.appendChild(optionItem)}optgroupItem.appendChild(nestedOptionsList),nestedList.appendChild(optgroupItem)}else{const options=originalSelect.getElementsByTagName("option");for(let j=0;j<options.length;j++){const option=options[j],optionItem=document.createElement("div");optionItem.classList.add("tail--nested-dropdown-item");const optionCheckbox=document.createElement("input");optionCheckbox.type="checkbox",optionCheckbox.value=option.textContent;const optionLabel=document.createElement("label"),optionLabelText=document.createElement("span");optionLabelText.textContent=option.textContent,option.dataset.description&&(optionLabelText.innerHTML+=`<small>${option.dataset.description}</small>`),option.selected&&option.hasAttribute("selected")&&(optionCheckbox.checked=!0,updateCounter(originalSelect),updateCustomTextInput(originalSelect)),optionLabel.appendChild(optionCheckbox),optionLabel.appendChild(optionLabelText),optionItem.appendChild(optionLabel),nestedList.appendChild(optionItem)}}customDropdown.setAttribute("role","combobox"),customDropdown.setAttribute("aria-haspopup","true"),customDropdown.setAttribute("aria-expanded","false"),attachOptionCheckboxListeners(),nestedList.appendChild(fragment)}function toggleAll(originalSelect,toggleAllCheckbox){const isChecked=toggleAllCheckbox.checked,optionCheckboxes=nestedList.querySelectorAll('input[type="checkbox"]');optionCheckboxes.forEach(checkbox=>{checkbox.checked=isChecked,updateOriginalOptionState(originalSelect,checkbox)})}function uncheckAll(originalSelect){const optionCheckboxes=nestedList.querySelectorAll('input[type="checkbox"]');optionCheckboxes.forEach(checkbox=>{checkbox.checked=!1,updateOriginalOptionState(originalSelect,checkbox)});const originalOptions=originalSelect.getElementsByTagName("option");for(let i=0;i<originalOptions.length;i++)originalOptions[i].selected=!1}function toggleOption(checkbox){if(originalSelect.multiple)updateOriginalOptionState(originalSelect,checkbox);else{const optionCheckboxes=nestedList.querySelectorAll('.tail--nested-dropdown-item input[type="checkbox"]');optionCheckboxes.forEach(cb=>cb.checked=!1),checkbox.checked=!0,updateOriginalOptionState(originalSelect,checkbox)}}function toggleOptgroup(optgroupCheckbox){const isChecked=optgroupCheckbox.checked,nestedOptionsList=optgroupCheckbox.closest(".tail--optgroup").querySelector(".tail--nested-dropdown-list"),optionCheckboxes=nestedOptionsList.querySelectorAll('input[type="checkbox"]');if(optionCheckboxes.forEach(checkbox=>{checkbox.checked=isChecked,toggleOption(checkbox)}),!originalSelect.multiple){const customDropdown=originalSelect.closest(".tail-select");if(customDropdown){const otherOptgroupCheckboxes=customDropdown.querySelectorAll('.tail--nested-dropdown-item input[type="checkbox"]');otherOptgroupCheckboxes.forEach(cb=>{cb!==optgroupCheckbox&&(cb.checked=!1,updateOriginalOptionState(originalSelect,cb))})}}updateOriginalOptionState(originalSelect,optgroupCheckbox)}function attachOptionCheckboxListeners(){const optionCheckboxes=nestedList.querySelectorAll('.tail--nested-dropdown-item input[type="checkbox"]');optionCheckboxes.forEach(checkbox=>{checkbox.addEventListener("change",()=>toggleOption(checkbox))})}function updateOriginalOptionState(originalSelect,checkbox,customDropdown){const optionValue=checkbox.value,option=Array.from(originalSelect.options).find(opt=>opt.value===optionValue||opt.textContent===optionValue);if(option){checkbox.checked?option.selected=!0:option.selected=!1;const event=new Event("change",{bubbles:!0});originalSelect.dispatchEvent(event)}const selectedOptions=Array.from(originalSelect.options).filter(opt=>opt.selected);originalSelect.multiple?searchInput.value=selectedOptions.map(opt=>opt.textContent).join(", "):selectedOptions.length>0&&searchInput?searchInput.value=selectedOptions[0].textContent:searchInput.value="",opts.multiTags&&originalSelect.multiple&&updateSelectedOptionsList(selectedOptionsList,selectedOptions);const selectedValues=selectedOptions.map(opt=>opt.value);for(var options=originalSelect.options,count=0,i=0;i<options.length;i++)options[i].selected&&count++;if(opts.multiCounter){let customId=originalSelect.id;if(customId){let counterElement=document.querySelector(`.${customId}`).querySelector(".tail--counter");counterElement&&(counterElement.textContent=count)}}}function filterOptions(originalSelect,searchInput){const searchTerm=searchInput.value.trim().toLowerCase(),optionItems=nestedList.querySelectorAll("div");optionItems.forEach(optionItem=>{const optionCheckbox=optionItem.querySelector('input[type="checkbox"]'),optionLabel=optionCheckbox.nextElementSibling.textContent.toLowerCase(),optgroupItem=optionItem.closest("div");optionCheckbox.style.display=optionLabel.includes(searchTerm)?"inline-block":"none"}),optionItems.forEach(optionItem=>{const optionCheckbox=optionItem.querySelector('input[type="checkbox"]'),optgroupItem=optionItem.closest("div"),nestedCheckboxes=optionItem.querySelectorAll('div input[type="checkbox"]:not([style="display: none;"])'),hasVisibleNestedCheckboxes=nestedCheckboxes.length>0;optgroupItem.style.display="inline-block"===optionCheckbox.style.display||hasVisibleNestedCheckboxes?"block":"none"})}function updateSelectedOptionsList(selectedOptionsList,selectedOptions){selectedOptionsList.innerHTML="",selectedOptions.forEach(opt=>{const listItem=document.createElement("li");listItem.textContent=opt.textContent,selectedOptionsList.appendChild(listItem)})}function updateCustomTextInput(originalSelect){const selectedOptions=Array.from(originalSelect.options).filter(opt=>opt.selected&&opt.hasAttribute("selected"));originalSelect.multiple?searchInput.value=selectedOptions.map(opt=>opt.textContent).join(", "):selectedOptions.length>0&&searchInput?searchInput.value=selectedOptions[0].textContent:searchInput.value="",opts.multiTags&&originalSelect.multiple&&updateSelectedOptionsList(selectedOptionsList,selectedOptions)}function updateCounter(originalSelect){let customId=originalSelect.id;if(customId){let counterElement=document.querySelector(`.${customId}`).querySelector(".tail--counter");if(counterElement){const count=Array.from(originalSelect.options).filter(opt=>opt.selected).length;counterElement.textContent=count}}}function toggleDropdownVisibility(){nestedList.style.display="block",customDropdown.setAttribute("aria-expanded","true")}function hideDropdown(){nestedList.style.display="none",customDropdown.setAttribute("aria-expanded","false")}function handleClickOutside(event){customDropdown.contains(event.target)||hideDropdown()}function handleKeyDown(event){"Escape"===event.key&&hideDropdown()}selectedOptionsList.classList.add("tail--selected-options-list"),opts.multiTags&&originalSelect.multiple&&customDropdown.insertAdjacentElement("afterend",selectedOptionsList),searchInput.addEventListener("focus",toggleDropdownVisibility),document.addEventListener("click",handleClickOutside),document.addEventListener("keydown",handleKeyDown),buildNestedList()})}};
|
@@ -0,0 +1,254 @@
|
|
1
|
+
:root {
|
2
|
+
--option-size: 14px;
|
3
|
+
|
4
|
+
--tail-select--text: #000000;
|
5
|
+
--tail-select--meta: #d1d5db;
|
6
|
+
--tail-select--muted: #b3b3b3;
|
7
|
+
--tail-select--background: #ffffff;
|
8
|
+
|
9
|
+
--tail-select--accent: #3742fa;
|
10
|
+
|
11
|
+
--tail-select--selection: rgba(0, 0, 0, 0.05);
|
12
|
+
--tail-select--selection-text: #ffffff;
|
13
|
+
--tail-select--selection-muted: rgb(147 197 253 / 1);
|
14
|
+
}
|
15
|
+
|
16
|
+
[data-theme="tail-theme--light"] {
|
17
|
+
color-scheme: light;
|
18
|
+
}
|
19
|
+
|
20
|
+
[data-theme="tail-theme--dark"] {
|
21
|
+
color-scheme: dark;
|
22
|
+
|
23
|
+
--tail-select--text: #ffffff;
|
24
|
+
--tail-select--meta: #30343b;
|
25
|
+
--tail-select--muted: #4d4d4d;
|
26
|
+
--tail-select--background: #191b1f;
|
27
|
+
|
28
|
+
--tail-select--accent: #3742fa;
|
29
|
+
|
30
|
+
--tail-select--selection: rgba(0, 0, 0, 0.15);
|
31
|
+
--tail-select--selection-text: #ffffff;
|
32
|
+
--tail-select--selection-muted: #353b48;
|
33
|
+
}
|
34
|
+
|
35
|
+
.tail-select * {
|
36
|
+
box-sizing: border-box;
|
37
|
+
}
|
38
|
+
|
39
|
+
.tail-select,
|
40
|
+
.tail-select.tail--single,
|
41
|
+
.tail-select.tail--multiple {
|
42
|
+
position: relative;
|
43
|
+
display: flex;
|
44
|
+
align-items: center;
|
45
|
+
user-select: none;
|
46
|
+
|
47
|
+
width: fit-content;
|
48
|
+
height: fit-content;
|
49
|
+
font-size: 14px;
|
50
|
+
line-height: normal;
|
51
|
+
font-family: inherit;
|
52
|
+
|
53
|
+
cursor: default;
|
54
|
+
color: var(--tail-select--text);
|
55
|
+
padding: 6px 6px 6px 6px;
|
56
|
+
text-align: left;
|
57
|
+
background-color: var(--tail-select--background);
|
58
|
+
border: 1px solid var(--tail-select--meta);
|
59
|
+
border-radius: 3px;
|
60
|
+
box-shadow: 0 1px 1px 1px rgba(0, 0, 0, 0.05);
|
61
|
+
|
62
|
+
transition: border 0.4s ease-out;
|
63
|
+
}
|
64
|
+
.tail-select:hover {
|
65
|
+
border-color: var(--tail-select--accent);
|
66
|
+
}
|
67
|
+
|
68
|
+
.tail-select .tail--search,
|
69
|
+
.tail-select input[type="text"].tail--search {
|
70
|
+
font-family: inherit;
|
71
|
+
color: var(--tail-select--text);
|
72
|
+
border: 0 none;
|
73
|
+
border-radius: initial;
|
74
|
+
border-width: 0;
|
75
|
+
border-color: transparent;
|
76
|
+
background-color: transparent;
|
77
|
+
display: inline-block;
|
78
|
+
width: 100%;
|
79
|
+
outline: 0;
|
80
|
+
font-size: 13px;
|
81
|
+
margin: 0;
|
82
|
+
padding: 2px 72px 2px 2px;
|
83
|
+
cursor: text;
|
84
|
+
}
|
85
|
+
.tail-select .tail--search:hover,
|
86
|
+
.tail-select input[type="text"].tail--search:hover {
|
87
|
+
border: 0 none;
|
88
|
+
}
|
89
|
+
|
90
|
+
.tail-select.tail--single .tail--search,
|
91
|
+
.tail-select.tail--single input[type="text"].tail--search {
|
92
|
+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24'%3E%3Cpath fill='%23808080' d='M5.7 9.71a1 1 0 0 0 0 1.41l4.9 4.9a2 2 0 0 0 2.83 0l4.89-4.9a1 1 0 1 0-1.42-1.41l-4.18 4.18a1 1 0 0 1-1.42 0L7.12 9.71a1 1 0 0 0-1.41 0Z'/%3E%3C/svg%3E");
|
93
|
+
background-position: 100%, 100% 0;
|
94
|
+
background-size: 1.5em 1.5em;
|
95
|
+
background-repeat: no-repeat;
|
96
|
+
}
|
97
|
+
.tail-select.tail--single .tail--search:focus,
|
98
|
+
.tail-select.tail--single input[type="text"].tail--search:focus {
|
99
|
+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24'%3E%3Cpath fill='%23808080' d='M18.3 15.29a1 1 0 0 0 0-1.41l-4.9-4.9a2 2 0 0 0-2.83 0l-4.89 4.9a1 1 0 1 0 1.42 1.41l4.18-4.18a1 1 0 0 1 1.42 0l4.18 4.18a1 1 0 0 0 1.41 0Z'/%3E%3C/svg%3E");
|
100
|
+
}
|
101
|
+
.tail-select.tail--multiple .tail--search,
|
102
|
+
.tail-select.tail--multiple input[type="text"].tail--search {
|
103
|
+
width: calc(100% - 32px);
|
104
|
+
}
|
105
|
+
|
106
|
+
.tail-select .tail--toolbar {
|
107
|
+
position: absolute;
|
108
|
+
top: 50%;
|
109
|
+
right: 6px;
|
110
|
+
transform: translateY(-50%);
|
111
|
+
}
|
112
|
+
|
113
|
+
.tail-select .tail--counter {
|
114
|
+
font-size: 12px;
|
115
|
+
text-align: center;
|
116
|
+
color: var(--tail-select--text);
|
117
|
+
font-weight: 700;
|
118
|
+
width: 24px;
|
119
|
+
display: inline-block;
|
120
|
+
border-right: 1px solid var(--tail-select--muted);
|
121
|
+
padding: 0 0px 0 0;
|
122
|
+
margin: 0 8px 0 0;
|
123
|
+
}
|
124
|
+
|
125
|
+
.tail-select label.all,
|
126
|
+
.tail-select button.uncheck {
|
127
|
+
width: auto;
|
128
|
+
height: auto;
|
129
|
+
margin: 0 2px;
|
130
|
+
padding: 2px 6px;
|
131
|
+
display: inline-block;
|
132
|
+
font-family: inherit;
|
133
|
+
font-size: 10px;
|
134
|
+
line-height: 14px;
|
135
|
+
text-shadow: none;
|
136
|
+
letter-spacing: 0;
|
137
|
+
text-transform: none;
|
138
|
+
vertical-align: top;
|
139
|
+
border-width: 1px;
|
140
|
+
border-style: solid;
|
141
|
+
border-color: transparent;
|
142
|
+
border-radius: 3px;
|
143
|
+
box-shadow: none;
|
144
|
+
|
145
|
+
color: var(--tail-select--text);
|
146
|
+
border-color: var(--tail-select--meta);
|
147
|
+
background-color: transparent;
|
148
|
+
backdrop-filter: blur(2px);
|
149
|
+
}
|
150
|
+
.tail-select label.all input[type="checkbox"] {
|
151
|
+
display: none;
|
152
|
+
}
|
153
|
+
.tail-select label.all:hover,
|
154
|
+
.tail-select button.uncheck:hover {
|
155
|
+
color: var(--tail-select--text);
|
156
|
+
border-color: var(--tail-select--text);
|
157
|
+
}
|
158
|
+
|
159
|
+
|
160
|
+
.tail-select > .tail--nested-dropdown {
|
161
|
+
position: absolute;
|
162
|
+
top: 100%;
|
163
|
+
left: 0;
|
164
|
+
z-index: 1;
|
165
|
+
background-color: var(--tail-select--background);
|
166
|
+
border: 1px solid var(--tail-select--meta);
|
167
|
+
border-radius: 3px;
|
168
|
+
box-shadow: 0 1px 1px 1px rgba(0, 0, 0, 0.05);
|
169
|
+
|
170
|
+
padding: 8px 0;
|
171
|
+
margin: 6px 0 0 0;
|
172
|
+
max-height: 200px;
|
173
|
+
overflow: auto;
|
174
|
+
width: 100%;
|
175
|
+
}
|
176
|
+
|
177
|
+
.tail-select .tail--optgroup {
|
178
|
+
padding: 0;
|
179
|
+
cursor: default;
|
180
|
+
}
|
181
|
+
.tail-select .tail--nested-dropdown-list {
|
182
|
+
padding: 0;
|
183
|
+
}
|
184
|
+
.tail-select .tail--nested-dropdown label {
|
185
|
+
display: flex;
|
186
|
+
padding: 6px 12px;
|
187
|
+
align-items: start;
|
188
|
+
}
|
189
|
+
.tail-select .tail--nested-dropdown label input[type="checkbox"] {
|
190
|
+
display: block;
|
191
|
+
}
|
192
|
+
|
193
|
+
.tail-select .tail--nested-dropdown-item {
|
194
|
+
color: var(--tail-select--text);
|
195
|
+
list-style: none;
|
196
|
+
padding: 0;
|
197
|
+
display: block;
|
198
|
+
cursor: default;
|
199
|
+
}
|
200
|
+
.tail-select .tail--nested-dropdown-item span {
|
201
|
+
font-size: 14px;
|
202
|
+
line-height: 1.5;
|
203
|
+
}
|
204
|
+
.tail-select .tail--nested-dropdown-item span small {
|
205
|
+
display: block;
|
206
|
+
font-size: 12px;
|
207
|
+
}
|
208
|
+
|
209
|
+
.tail-select .tail--nested-dropdown-item:hover {
|
210
|
+
background-color: var(--tail-select--selection);
|
211
|
+
}
|
212
|
+
|
213
|
+
|
214
|
+
.tail--optgroup-label {
|
215
|
+
font-weight: 700;
|
216
|
+
}
|
217
|
+
|
218
|
+
.tail-select input[type="checkbox"] {
|
219
|
+
height: var(--option-size);
|
220
|
+
width: var(--option-size);
|
221
|
+
padding: 0;
|
222
|
+
margin: 4px 0.5rem 0 0;
|
223
|
+
|
224
|
+
accent-color: #3742fa;
|
225
|
+
}
|
226
|
+
|
227
|
+
.tail-select input[type="checkbox"]:focus {
|
228
|
+
outline: 3px solid rgb(0 87 255 / 50%);
|
229
|
+
outline: 3px solid rgb(147 197 253 / 1);
|
230
|
+
outline-offset: 0px;
|
231
|
+
}
|
232
|
+
|
233
|
+
.tail-select.tail--single .tail--counter,
|
234
|
+
.tail--single .tail--optgroup > label > input[type="checkbox"],
|
235
|
+
.tail--single .tail--toolbar {
|
236
|
+
display: none;
|
237
|
+
}
|
238
|
+
|
239
|
+
.tail--selected-options-list {
|
240
|
+
padding: 0;
|
241
|
+
margin: 0;
|
242
|
+
display: flex;
|
243
|
+
gap: 2px;
|
244
|
+
}
|
245
|
+
.tail--selected-options-list li {
|
246
|
+
list-style: none;
|
247
|
+
padding: 8px 12px;
|
248
|
+
font-size: 13px;
|
249
|
+
line-height: 1;
|
250
|
+
margin: 0;
|
251
|
+
border-radius: 48px;
|
252
|
+
background-color: var(--tail-select--accent);
|
253
|
+
color: var(--tail-select--selection-text);
|
254
|
+
}
|
metadata
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tail-select-rails
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Gajendra Jena
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2024-06-03 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '6.1'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '6.1'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: jquery-rails
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: sass-rails
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: uglifier
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description: Rails gem for easy tail-select-rails (v1.0.2) integration. See https://getbutterfly.com/tail-select/
|
70
|
+
for more information on how to use tail.select
|
71
|
+
email: gaju.mca@gmail.com
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- LICENSE
|
77
|
+
- Readme.md
|
78
|
+
- lib/tail_select_rails.rb
|
79
|
+
- vendor/assets/javascripts/tail-select-rails.js
|
80
|
+
- vendor/assets/javascripts/tail-select-rails.min.js
|
81
|
+
- vendor/assets/stylesheets/tail-select-rails.css
|
82
|
+
homepage: http://github.com/gajendrajena/tail-select-rails
|
83
|
+
licenses: []
|
84
|
+
metadata: {}
|
85
|
+
post_install_message:
|
86
|
+
rdoc_options: []
|
87
|
+
require_paths:
|
88
|
+
- lib
|
89
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
95
|
+
requirements:
|
96
|
+
- - ">="
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: '0'
|
99
|
+
requirements: []
|
100
|
+
rubygems_version: 3.2.3
|
101
|
+
signing_key:
|
102
|
+
specification_version: 4
|
103
|
+
summary: Rails gem for easy tail-select-rails integration.
|
104
|
+
test_files: []
|