I want to share this tweak to add feature filtered search in Admin for Dropdown and Checkbox with Multiple Value in Couchcms admin Panel
These are the script:
How to:
1. Add the script to <cms:template> couchcms using <cms:config_form_view> include with styling css
2. Adjust the necessary class/id or name of your template and save.
3. Visit your frontend clonable template (for registering the template)
4. Finish, if all the requirements correct, your filter search will be appear on the admin panel.
Don't forget to adjust the script based on Class or ID on your admin template e.g:
Please adjust this part: id/class of your dropdown/checkbox template (#k_element_poi1)
And also this part: name of your checkbox (name="f_poi1[]"):
Note:
Please adjust: f_[your-dropdown or checkbox id/class]
Here's the working screenshot:
Filtered Checkbox with Multiple value:
Filtered Dropdown:
These are the script:
- Code: Select all
<cms:config_form_view>
<cms:script>
//FOR CHECKBOX
(function($) {
$(document).ready(function() {
console.log("Custom script for checkboxes loaded");
// Create search and input fields
const searchField = $('<input type="text" id="searchInput" placeholder="Cari point of interest">');
const inputField = $('<input type="hidden" id="categoryInput" name="f_poi1[]" value="">'); // Hidden input to store selected values
// Create container for selected tags
const selectedTagsContainer = $('<div class="selected-tags"></div>').insertBefore(searchField);
// Find the checkbox container
const checkboxContainer = $('#k_element_poi1 .ctrls-checkbox');
// Append search field and selected tags container to the container
checkboxContainer.before(searchField);
checkboxContainer.before(selectedTagsContainer);
// Hide all original checkboxes
checkboxContainer.find('label').hide();
// Create dropdown-like menu for checkboxes
const dropdownMenu = $('<div class="dropdown-menu"></div>').appendTo('body');
//const dropdownSelect = $('<div class="dropdown-select">Pilih point of interest</div>').appendTo(dropdownMenu);
const dropdownItems = $('<div class="dropdown-items"></div>').appendTo(dropdownMenu);
// Populate dropdown items
checkboxContainer.find('label').each(function() {
const checkbox = $(this).find('input[type="checkbox"]');
const text = $(this).text().trim();
const dropdownItem = $(`<div class="dropdown-item" data-value="${checkbox.val()}">${text}</div>`).appendTo(dropdownItems);
// Handle click on dropdown item
dropdownItem.on('click', function() {
const value = $(this).data('value');
const isChecked = checkbox.prop('checked');
checkbox.prop('checked', !isChecked).trigger('change');
updateSelectedTags(); // Update selected tags display
updateCheckboxVisibility(); // Update checkbox visibility
dropdownMenu.hide(); // Hide dropdown menu after selection
searchField.val('').trigger('input'); // Clear and reset search field
updateHiddenInput(); // Update hidden input with selected values
});
});
// Append dropdown menu to document body
dropdownMenu.appendTo('body');
// Event listener for input field to add new checkboxes
inputField.on('change', function() {
const values = $(this).val().split('|').map(v => v.trim()).filter(v => v !== '');
const currentCheckboxes = checkboxContainer.find('input[type="checkbox"]');
currentCheckboxes.prop('checked', false);
values.forEach(value => {
let matchingCheckbox = currentCheckboxes.filter((_, checkbox) => $(checkbox).val().trim() === value);
if (matchingCheckbox.length === 0) {
const newCheckbox = $('<input type="checkbox" name="f_poi1[]" checked>').val(value);
const newLabel = $('<label>').append(newCheckbox).append('<span class="ctrl-option"></span>').append(value);
checkboxContainer.append(newLabel).append('<br>');
} else {
matchingCheckbox.prop('checked', true);
}
});
updateCheckboxVisibility(); // Update checkbox visibility after updating
updateSelectedTags(); // Update selected tags display
updateHiddenInput(); // Update hidden input with selected values
});
// Event listener for search field to filter dropdown items
searchField.on('input', function() {
const searchValue = $(this).val().toLowerCase();
const dropdownItems = dropdownMenu.find('.dropdown-item');
dropdownItems.each(function() {
const text = $(this).text().toLowerCase();
if (text.includes(searchValue)) {
$(this).show();
} else {
$(this).hide();
}
});
// Toggle dropdown menu visibility based on search value
if (searchValue.length > 0) {
positionDropdown(); // Reposition dropdown if shown
dropdownMenu.show();
checkboxContainer.find('label').hide(); // Hide all checkbox labels
} else {
dropdownMenu.hide();
updateCheckboxVisibility(); // Update checkbox visibility based on selected items
}
});
// Event listener for clicking on search field to show all dropdown items
searchField.on('click', function() {
dropdownItems.children('.dropdown-item').show(); // Show all dropdown items
dropdownMenu.show(); // Show dropdown menu
positionDropdown(); // Reposition dropdown if shown
checkboxContainer.find('label').hide(); // Hide all checkbox labels
});
// Event listener for checkbox clicks to reorder and update inputField
checkboxContainer.on('change', 'input[type="checkbox"]', function() {
updateSelectedTags(); // Update selected tags display
updateCheckboxVisibility(); // Update checkbox visibility after changing
updateHiddenInput(); // Update hidden input with selected values
});
// Function to update selected tags display
function updateSelectedTags() {
selectedTagsContainer.empty();
const selectedValues = getSelectedValues();
selectedValues.forEach((value, index) => {
const tag = $(`<div class="selected-tag">${value} <span class="remove-tag">×</span></div>`);
selectedTagsContainer.append(tag);
// Add remove functionality
tag.find('.remove-tag').on('click', function() {
const checkbox = checkboxContainer.find(`input[type="checkbox"][value="${value}"]`);
checkbox.prop('checked', false).trigger('change');
updateSelectedTags();
updateCheckboxVisibility();
updateHiddenInput();
});
});
}
// Function to get selected checkbox values
function getSelectedValues() {
return checkboxContainer.find('input[type="checkbox"]:checked').map((_, checkbox) => $(checkbox).val().trim()).get();
}
// Function to update checkbox visibility based on selected items
function updateCheckboxVisibility() {
const selectedValues = getSelectedValues();
checkboxContainer.find('label').each(function() {
const checkbox = $(this).find('input[type="checkbox"]');
const value = checkbox.val().trim();
if (selectedValues.includes(value)) {
$(this).show();
} else {
$(this).hide();
}
});
}
// Function to reposition dropdown menu
function positionDropdown() {
const searchOffset = searchField.offset();
dropdownMenu.css({
top: searchOffset.top + searchField.outerHeight(),
left: searchOffset.left,
width: searchField.outerWidth(),
zIndex: 999 // Adjust z-index as needed
});
}
// Function to update hidden input with selected values
function updateHiddenInput() {
const selectedValues = getSelectedValues();
inputField.val(selectedValues.join('|')); // Use | for backend processing
}
// Close dropdown menu if clicked outside
$(document).on('click', function(event) {
if (!$(event.target).closest('.dropdown-menu').length && !$(event.target).is(searchField)) {
dropdownMenu.hide();
}
});
// Initialize on page load
updateSelectedTags(); // Update selected tags display
updateCheckboxVisibility(); // Update checkbox visibility
updateHiddenInput(); // Update hidden input with selected values
});
})(jQuery);
//FOR DROPDOWN
(function($) {
$(document).ready(function() {
console.log("Custom script loaded");
const selectNames = ["f_kecamatan", "f_kab_kota", "f_provinsi", "f_owner_space_chk" , "f_promo_space_chk"];
let currentOpenDropdown = null; // Variable to track the currently open dropdown
selectNames.forEach(name => {
const dropdowns = $(`select[name="${name}"]`);
if (dropdowns.length > 0) {
dropdowns.each((index, dropdown) => {
createCustomDropdown($(dropdown));
});
}
});
function createCustomDropdown($dropdown) {
const options = $dropdown.find('option');
const optionsArr = Array.from(options).map(option => ({
value: option.value,
text: option.textContent,
selected: option.selected
}));
const customDropdown = $('<div class="dropdown-filter"></div>').insertAfter($dropdown);
const selectedOption = optionsArr.find(option => option.selected) || optionsArr[0];
const selected = $('<div class="dropdown-select"></div>').text(selectedOption.text).appendTo(customDropdown);
const menu = $('<div class="dropdown-menu"></div>').appendTo(customDropdown);
selected.on("click", function() {
// Close the currently open dropdown if it's not the same as the clicked one
if (currentOpenDropdown && currentOpenDropdown !== menu) {
currentOpenDropdown.hide();
}
toggleDropdown.call(menu);
});
const search = $('<input type="text" class="dropdown-menu-search" placeholder="Search...">').appendTo(menu);
const menuInnerWrapper = $('<div class="dropdown-menu-inner"></div>').appendTo(menu);
// Create a document fragment to batch the DOM updates
const fragment = document.createDocumentFragment();
optionsArr.forEach(option => {
const item = $('<div class="dropdown-menu-item"></div>')
.data('value', option.value)
.text(option.text);
if (option.selected) {
item.addClass('is-select');
}
item.on("click", setSelected.bind(item, selected, $dropdown, menu));
fragment.appendChild(item[0]);
});
menuInnerWrapper.append(fragment);
menuInnerWrapper.children().first().addClass("selected");
search.on("input", debounce(filterItems.bind(search, optionsArr, menuInnerWrapper), 300));
$(document).on("click", closeIfClickedOutside.bind(customDropdown, menu));
$dropdown.hide();
}
function toggleDropdown() {
if (this.is(':visible')) {
this.hide();
currentOpenDropdown = null;
} else {
this.show();
this.find("input").focus();
currentOpenDropdown = this; // Track the currently open dropdown
}
}
function setSelected(selected, $dropdown, menu) {
const value = this.data('value');
const label = this.text();
selected.text(label);
$dropdown.val(value).change();
menu.hide();
currentOpenDropdown = null; // Clear the currently open dropdown
menu.find("input").val('');
menu.find('.dropdown-menu-inner div').each(function() {
$(this).show();
});
menu.find('.is-select').removeClass('is-select');
this.addClass('is-select');
}
function filterItems(itemsArr, menuInnerWrapper) {
const value = this.val().toLowerCase();
menuInnerWrapper.children().each(function() {
const itemText = $(this).text().toLowerCase();
$(this).toggle(itemText.includes(value));
});
}
function closeIfClickedOutside(menu, e) {
if (!$(e.target).closest(".dropdown-filter").length && menu.is(':visible')) {
menu.hide();
currentOpenDropdown = null; // Clear the currently open dropdown
}
}
function debounce(func, wait) {
let timeout;
return function(...args) {
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(this, args), wait);
};
}
});
})(jQuery);
</cms:script>
<cms:style>
.dropdown-menu,.dropdown-menu-search,.dropdown-select{width:100%;box-sizing:border-box}.dropdown-menu-item,.dropdown-select{cursor:pointer;box-sizing:border-box}.dropdown-filter{position:relative;display:inline-block;width:100%;min-width:300px}.dropdown-menu input[type=text]{border:0;border-bottom:1px solid #ccc;border-radius:0}.dropdown-select{background:#fff;border:1px solid #ccc;padding:10px;border-radius:4px;box-shadow:0 1px 3px rgba(0,0,0,.1)}.dropdown-menu{display:none;position:absolute;background-color:#fff;border:1px solid #ccc;max-height:200px;overflow-y:auto;z-index:1000;box-shadow:0 4px 6px rgba(0,0,0,.1);border-radius:4px}.dropdown-menu-search{padding:10px;border-bottom:1px solid #ccc;outline:0;border-top-left-radius:4px;border-top-right-radius:4px}.dropdown-menu-inner{padding:0;margin:0}.dropdown-menu-item{padding:10px;border-bottom:1px solid #eee}.dropdown-menu-item:last-child{border-bottom:none}.dropdown-menu-item.is-select,.dropdown-menu-item:hover{background-color:#f0f0f0}select[name=kab_kota]{display:none}.admin-panel .dropdown-filter{width:auto;max-width:300px}.admin-panel .dropdown-menu-item,.admin-panel .dropdown-menu-search,.admin-panel .dropdown-select{font-size:14px}
/* Hide the input field when items are selected */
#categoryInput {
display: none;
}
#searchInput{width:100%}
/* Style for selected tags */
.selected-tags {
margin-top: 10px;
display: flex;
flex-wrap: wrap;
}
.selected-tag {
background-color: #007bff;
color: #fff;
padding: 4px 8px;
margin-right: 5px;
margin-bottom: 5px;
border-radius: 4px;
cursor: pointer;
font-size:14px;
}
.selected-tag:hover {
background-color: #0056b3;
}
/* Style for dropdown menu */
.dropdown-menu {
position: absolute;
background-color: #f9f9f9;
border: 1px solid #ccc;
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
z-index: 999;
display: none;
}
.dropdown-select {
/*padding: 8px;
background-color: #007bff;
color: #fff;*/
cursor: pointer;
}
.dropdown-items {
max-height: 200px;
overflow-y: auto;
overflow-x: hidden;
}
.dropdown-item {
padding: 8px;
cursor: pointer;
}
.dropdown-item:hover {
background-color: #e9ecef;
}
/* Hide checkboxes when dropdown menu is active */
#k_element_poi1 .ctrls-checkbox {
display: none !important;
visibility:hidden;
opacity:0;
}
</cms:style>
</cms:config_form_view>
How to:
1. Add the script to <cms:template> couchcms using <cms:config_form_view> include with styling css
2. Adjust the necessary class/id or name of your template and save.
3. Visit your frontend clonable template (for registering the template)
4. Finish, if all the requirements correct, your filter search will be appear on the admin panel.
Don't forget to adjust the script based on Class or ID on your admin template e.g:
Please adjust this part: id/class of your dropdown/checkbox template (#k_element_poi1)
- Code: Select all
//For Checkbox
// Find the checkbox container
const checkboxContainer = $('#k_element_poi1 .ctrls-checkbox');
// For Dropdowns
const selectNames = ["f_kecamatan", "f_kab_kota", "f_provinsi", "f_owner_space_chk" , "f_promo_space_chk"];
let currentOpenDropdown = null; // Variable to track the currently open dropdown
And also this part: name of your checkbox (name="f_poi1[]"):
- Code: Select all
const inputField = $('<input type="hidden" id="categoryInput" name="f_poi1[]" value="">'); // Hidden input to store selected values
const newCheckbox = $('<input type="checkbox" name="f_poi1[]" checked>').val(value);
Note:
Please adjust: f_[your-dropdown or checkbox id/class]
Here's the working screenshot:
Filtered Checkbox with Multiple value:
Filtered Dropdown: