Виджет для формы выбора зеркал
This commit is contained in:
160
dbapp/mainapp/static/css/checkbox-select-multiple.css
Normal file
160
dbapp/mainapp/static/css/checkbox-select-multiple.css
Normal file
@@ -0,0 +1,160 @@
|
||||
.checkbox-multiselect-wrapper {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.multiselect-input-container {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-height: 38px;
|
||||
border: 1px solid #ced4da;
|
||||
border-radius: 0.25rem;
|
||||
background-color: #fff;
|
||||
cursor: text;
|
||||
padding: 4px 30px 4px 4px;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.multiselect-input-container:focus-within {
|
||||
border-color: #86b7fe;
|
||||
outline: 0;
|
||||
box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
|
||||
}
|
||||
|
||||
.multiselect-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.multiselect-tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
background-color: #e9ecef;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 0.25rem;
|
||||
padding: 2px 8px;
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.5;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.multiselect-tag-remove {
|
||||
margin-left: 6px;
|
||||
cursor: pointer;
|
||||
color: #6c757d;
|
||||
font-weight: bold;
|
||||
border: none;
|
||||
background: none;
|
||||
padding: 0;
|
||||
font-size: 1rem;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.multiselect-tag-remove:hover {
|
||||
color: #dc3545;
|
||||
}
|
||||
|
||||
.multiselect-search {
|
||||
flex: 1 1 auto;
|
||||
min-width: 120px;
|
||||
border: none;
|
||||
outline: none;
|
||||
padding: 4px;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.multiselect-search:focus {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.multiselect-clear {
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 1.5rem;
|
||||
line-height: 1;
|
||||
color: #6c757d;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.multiselect-clear:hover {
|
||||
color: #dc3545;
|
||||
}
|
||||
|
||||
.multiselect-input-container.has-selections .multiselect-clear {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.multiselect-dropdown {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background-color: #fff;
|
||||
border: 1px solid #ced4da;
|
||||
border-radius: 0.25rem;
|
||||
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
z-index: 1000;
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Открытие вверх (по умолчанию) */
|
||||
.multiselect-dropdown {
|
||||
bottom: 100%;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
/* Открытие вниз (если места сверху недостаточно) */
|
||||
.multiselect-dropdown.dropdown-below {
|
||||
bottom: auto;
|
||||
top: 100%;
|
||||
margin-top: 2px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.multiselect-dropdown.show {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.multiselect-options {
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
.multiselect-option {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 8px 12px;
|
||||
cursor: pointer;
|
||||
margin: 0;
|
||||
transition: background-color 0.15s ease-in-out;
|
||||
}
|
||||
|
||||
.multiselect-option:hover {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
|
||||
.multiselect-option input[type="checkbox"] {
|
||||
margin-right: 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.multiselect-option .option-label {
|
||||
flex: 1;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.multiselect-option.hidden {
|
||||
display: none;
|
||||
}
|
||||
120
dbapp/mainapp/static/js/checkbox-select-multiple.js
Normal file
120
dbapp/mainapp/static/js/checkbox-select-multiple.js
Normal file
@@ -0,0 +1,120 @@
|
||||
/**
|
||||
* Checkbox Select Multiple Widget
|
||||
* Provides a multi-select dropdown with checkboxes and tag display
|
||||
*/
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Initialize all checkbox multiselect widgets
|
||||
document.querySelectorAll('.checkbox-multiselect-wrapper').forEach(function(wrapper) {
|
||||
initCheckboxMultiselect(wrapper);
|
||||
});
|
||||
});
|
||||
|
||||
function initCheckboxMultiselect(wrapper) {
|
||||
const widgetId = wrapper.dataset.widgetId;
|
||||
const inputContainer = wrapper.querySelector('.multiselect-input-container');
|
||||
const searchInput = wrapper.querySelector('.multiselect-search');
|
||||
const dropdown = wrapper.querySelector('.multiselect-dropdown');
|
||||
const tagsContainer = wrapper.querySelector('.multiselect-tags');
|
||||
const clearButton = wrapper.querySelector('.multiselect-clear');
|
||||
const checkboxes = wrapper.querySelectorAll('input[type="checkbox"]');
|
||||
|
||||
// Show dropdown when clicking on input container
|
||||
inputContainer.addEventListener('click', function(e) {
|
||||
if (e.target !== clearButton) {
|
||||
positionDropdown();
|
||||
dropdown.classList.add('show');
|
||||
searchInput.focus();
|
||||
}
|
||||
});
|
||||
|
||||
// Position dropdown (up or down based on available space)
|
||||
function positionDropdown() {
|
||||
const rect = inputContainer.getBoundingClientRect();
|
||||
const spaceAbove = rect.top;
|
||||
const spaceBelow = window.innerHeight - rect.bottom;
|
||||
const dropdownHeight = 300; // max-height from CSS
|
||||
|
||||
// If more space below and enough space, open downward
|
||||
if (spaceBelow > spaceAbove && spaceBelow >= dropdownHeight) {
|
||||
dropdown.classList.add('dropdown-below');
|
||||
} else {
|
||||
dropdown.classList.remove('dropdown-below');
|
||||
}
|
||||
}
|
||||
|
||||
// Hide dropdown when clicking outside
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!wrapper.contains(e.target)) {
|
||||
dropdown.classList.remove('show');
|
||||
}
|
||||
});
|
||||
|
||||
// Search functionality
|
||||
searchInput.addEventListener('input', function() {
|
||||
const searchTerm = this.value.toLowerCase();
|
||||
const options = wrapper.querySelectorAll('.multiselect-option');
|
||||
|
||||
options.forEach(function(option) {
|
||||
const label = option.querySelector('.option-label').textContent.toLowerCase();
|
||||
if (label.includes(searchTerm)) {
|
||||
option.classList.remove('hidden');
|
||||
} else {
|
||||
option.classList.add('hidden');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Handle checkbox changes
|
||||
checkboxes.forEach(function(checkbox) {
|
||||
checkbox.addEventListener('change', function() {
|
||||
updateTags();
|
||||
});
|
||||
});
|
||||
|
||||
// Clear all button
|
||||
clearButton.addEventListener('click', function(e) {
|
||||
e.stopPropagation();
|
||||
checkboxes.forEach(function(checkbox) {
|
||||
checkbox.checked = false;
|
||||
});
|
||||
updateTags();
|
||||
});
|
||||
|
||||
// Update tags display
|
||||
function updateTags() {
|
||||
tagsContainer.innerHTML = '';
|
||||
let hasSelections = false;
|
||||
|
||||
checkboxes.forEach(function(checkbox) {
|
||||
if (checkbox.checked) {
|
||||
hasSelections = true;
|
||||
const tag = document.createElement('div');
|
||||
tag.className = 'multiselect-tag';
|
||||
tag.innerHTML = `
|
||||
<span>${checkbox.dataset.label}</span>
|
||||
<button type="button" class="multiselect-tag-remove" data-value="${checkbox.value}">×</button>
|
||||
`;
|
||||
|
||||
// Remove tag on click
|
||||
tag.querySelector('.multiselect-tag-remove').addEventListener('click', function(e) {
|
||||
e.stopPropagation();
|
||||
checkbox.checked = false;
|
||||
updateTags();
|
||||
});
|
||||
|
||||
tagsContainer.appendChild(tag);
|
||||
}
|
||||
});
|
||||
|
||||
// Show/hide clear button
|
||||
if (hasSelections) {
|
||||
inputContainer.classList.add('has-selections');
|
||||
} else {
|
||||
inputContainer.classList.remove('has-selections');
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize tags on load
|
||||
updateTags();
|
||||
}
|
||||
Reference in New Issue
Block a user