Mass UI Updates
This commit is contained in:
@@ -0,0 +1,197 @@
|
||||
// Custom dropdown component with liquid glass styling
|
||||
// Replaces native select elements with styled custom dropdowns
|
||||
|
||||
/**
|
||||
* Initialize custom dropdowns for all select elements
|
||||
*/
|
||||
function initCustomDropdowns() {
|
||||
const selects = document.querySelectorAll('select');
|
||||
selects.forEach(select => {
|
||||
// Skip if already converted
|
||||
if (select.dataset.customDropdown === 'true') {
|
||||
return;
|
||||
}
|
||||
|
||||
createCustomDropdown(select);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a custom dropdown for a select element
|
||||
*/
|
||||
function createCustomDropdown(select) {
|
||||
// Mark as converted
|
||||
select.dataset.customDropdown = 'true';
|
||||
|
||||
// Create dropdown container
|
||||
const dropdownContainer = document.createElement('div');
|
||||
dropdownContainer.className = 'relative';
|
||||
|
||||
// Create button
|
||||
const button = document.createElement('button');
|
||||
button.type = 'button';
|
||||
button.className = 'w-full px-3 py-2 border-2 border-gray-300/60 rounded-xl backdrop-blur-sm bg-white/40 focus:ring-2 focus:ring-indigo-500/50 focus:border-indigo-500/70 outline-none text-base transition-all shadow-sm text-left flex items-center justify-between';
|
||||
button.id = select.id + '_button';
|
||||
|
||||
// Get selected option text
|
||||
const selectedOption = select.options[select.selectedIndex];
|
||||
const buttonText = document.createElement('span');
|
||||
buttonText.className = 'flex-1 text-gray-900';
|
||||
buttonText.textContent = selectedOption ? selectedOption.text : 'Select...';
|
||||
button.appendChild(buttonText);
|
||||
|
||||
// Add chevron icon
|
||||
const chevron = document.createElement('svg');
|
||||
chevron.className = 'w-5 h-5 text-gray-400 flex-shrink-0 ml-2 transition-transform';
|
||||
chevron.id = select.id + '_chevron';
|
||||
chevron.fill = 'none';
|
||||
chevron.stroke = 'currentColor';
|
||||
chevron.viewBox = '0 0 24 24';
|
||||
chevron.innerHTML = '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>';
|
||||
button.appendChild(chevron);
|
||||
|
||||
// Create dropdown menu
|
||||
const menu = document.createElement('div');
|
||||
menu.className = 'hidden absolute z-50 w-full mt-1 backdrop-blur-xl bg-white/80 rounded-2xl shadow-2xl border border-white/30 overflow-hidden max-h-60 overflow-y-auto';
|
||||
menu.id = select.id + '_menu';
|
||||
|
||||
// Create options
|
||||
Array.from(select.options).forEach((option, index) => {
|
||||
const menuItem = document.createElement('button');
|
||||
menuItem.type = 'button';
|
||||
menuItem.className = 'w-full text-left px-4 py-2.5 text-sm text-gray-700 hover:bg-white/40 focus:outline-none focus:bg-white/40 transition-colors';
|
||||
if (option.value === select.value) {
|
||||
menuItem.className += ' bg-indigo-50/50 text-indigo-600';
|
||||
}
|
||||
menuItem.textContent = option.text;
|
||||
menuItem.dataset.value = option.value;
|
||||
|
||||
menuItem.addEventListener('click', () => {
|
||||
select.value = option.value;
|
||||
buttonText.textContent = option.text;
|
||||
select.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
closeDropdown(select.id);
|
||||
|
||||
// Update selected state
|
||||
menu.querySelectorAll('button').forEach(item => {
|
||||
if (item.dataset.value === option.value) {
|
||||
item.className = 'w-full text-left px-4 py-2.5 text-sm text-indigo-600 bg-indigo-50/50 hover:bg-white/40 focus:outline-none focus:bg-white/40 transition-colors';
|
||||
} else {
|
||||
item.className = 'w-full text-left px-4 py-2.5 text-sm text-gray-700 hover:bg-white/40 focus:outline-none focus:bg-white/40 transition-colors';
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
menu.appendChild(menuItem);
|
||||
});
|
||||
|
||||
// Toggle dropdown
|
||||
button.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
const isOpen = !menu.classList.contains('hidden');
|
||||
|
||||
// Close all other dropdowns
|
||||
document.querySelectorAll('[id$="_menu"]').forEach(m => {
|
||||
if (m.id !== menu.id) {
|
||||
m.classList.add('hidden');
|
||||
const otherChevron = document.getElementById(m.id.replace('_menu', '_chevron'));
|
||||
if (otherChevron) {
|
||||
otherChevron.classList.remove('rotate-180');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (isOpen) {
|
||||
closeDropdown(select.id);
|
||||
} else {
|
||||
openDropdown(select.id);
|
||||
}
|
||||
});
|
||||
|
||||
// Close on outside click
|
||||
document.addEventListener('click', (e) => {
|
||||
if (!dropdownContainer.contains(e.target)) {
|
||||
closeDropdown(select.id);
|
||||
}
|
||||
});
|
||||
|
||||
// Wrap select and add custom elements
|
||||
select.style.display = 'none';
|
||||
select.style.position = 'absolute';
|
||||
select.style.opacity = '0';
|
||||
select.style.pointerEvents = 'none';
|
||||
dropdownContainer.appendChild(button);
|
||||
dropdownContainer.appendChild(menu);
|
||||
select.parentNode.insertBefore(dropdownContainer, select);
|
||||
dropdownContainer.appendChild(select);
|
||||
|
||||
// Listen for programmatic changes to select
|
||||
select.addEventListener('change', () => {
|
||||
updateCustomDropdown(select.id);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Open dropdown
|
||||
*/
|
||||
function openDropdown(selectId) {
|
||||
const menu = document.getElementById(selectId + '_menu');
|
||||
const chevron = document.getElementById(selectId + '_chevron');
|
||||
if (menu) {
|
||||
menu.classList.remove('hidden');
|
||||
if (chevron) {
|
||||
chevron.classList.add('rotate-180');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close dropdown
|
||||
*/
|
||||
function closeDropdown(selectId) {
|
||||
const menu = document.getElementById(selectId + '_menu');
|
||||
const chevron = document.getElementById(selectId + '_chevron');
|
||||
if (menu) {
|
||||
menu.classList.add('hidden');
|
||||
if (chevron) {
|
||||
chevron.classList.remove('rotate-180');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update custom dropdown when select value changes programmatically
|
||||
*/
|
||||
function updateCustomDropdown(selectId) {
|
||||
const select = document.getElementById(selectId);
|
||||
if (!select || select.dataset.customDropdown !== 'true') {
|
||||
return;
|
||||
}
|
||||
|
||||
const button = document.getElementById(selectId + '_button');
|
||||
const buttonText = button?.querySelector('span');
|
||||
const selectedOption = select.options[select.selectedIndex];
|
||||
|
||||
if (buttonText && selectedOption) {
|
||||
buttonText.textContent = selectedOption.text;
|
||||
}
|
||||
|
||||
// Update selected state in menu
|
||||
const menu = document.getElementById(selectId + '_menu');
|
||||
if (menu) {
|
||||
menu.querySelectorAll('button').forEach(item => {
|
||||
if (item.dataset.value === select.value) {
|
||||
item.className = 'w-full text-left px-4 py-2.5 text-sm text-indigo-600 bg-indigo-50/50 hover:bg-white/40 focus:outline-none focus:bg-white/40 transition-colors';
|
||||
} else {
|
||||
item.className = 'w-full text-left px-4 py-2.5 text-sm text-gray-700 hover:bg-white/40 focus:outline-none focus:bg-white/40 transition-colors';
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Make functions globally accessible
|
||||
if (typeof window !== 'undefined') {
|
||||
window.initCustomDropdowns = initCustomDropdowns;
|
||||
window.createCustomDropdown = createCustomDropdown;
|
||||
window.updateCustomDropdown = updateCustomDropdown;
|
||||
}
|
||||
Reference in New Issue
Block a user