Fixing bugs and docs issues

This commit is contained in:
Ronan Berder
2025-06-18 19:42:26 +08:00
parent a5c1b8df57
commit 218c16ba6b
19 changed files with 211 additions and 180 deletions
+1 -1
View File
@@ -504,7 +504,7 @@
/* Dropdown Menu */
@layer components {
.dropdown-menu {
@apply relative inline-flex p-1;
@apply relative inline-flex;
[data-popover] {
@apply p-1;
+18 -16
View File
@@ -16,7 +16,7 @@
id=None,
selected=None,
name=None,
items=None,
items=[],
main_attrs={},
trigger_attrs={},
popover_attrs={},
@@ -30,25 +30,27 @@
{% set first_option = namespace(item=None) %}
{% set selected_option = namespace(item=None) %}
{% for item in items %}
{% if item.type == "group" %}
{% for sub_item in item.items %}
{% if items %}
{% for item in items %}
{% if item.type == "group" %}
{% for sub_item in item.items %}
{% if not first_option.item %}
{% set first_option.item = sub_item %}
{% endif %}
{% if selected and sub_item.value == selected and not selected_option.item %}
{% set selected_option.item = sub_item %}
{% endif %}
{% endfor %}
{% else %}
{% if not first_option.item %}
{% set first_option.item = sub_item %}
{% set first_option.item = item %}
{% endif %}
{% if selected and sub_item.value == selected and not selected_option.item %}
{% set selected_option.item = sub_item %}
{% if selected and item.value == selected and not selected_option.item %}
{% set selected_option.item = item %}
{% endif %}
{% endfor %}
{% else %}
{% if not first_option.item %}
{% set first_option.item = item %}
{% endif %}
{% if selected and item.value == selected and not selected_option.item %}
{% set selected_option.item = item %}
{% endif %}
{% endif %}
{% endfor %}
{% endfor %}
{% endif %}
{% set default_option = selected_option.item or first_option.item or None %}
-10
View File
@@ -1,10 +0,0 @@
(() => {
document.body.addEventListener('click', (event) => {
const toggleButton = event.target.closest('[data-action="toggle-dark-mode"]');
if (!toggleButton) return;
const isDark = document.documentElement.classList.toggle('dark');
localStorage.setItem('darkMode', isDark ? 'dark' : 'light');
});
})();
+27 -19
View File
@@ -5,7 +5,11 @@
const menu = popover.querySelector('[role="menu"]');
if (!trigger || !menu || !popover) {
console.error('Dropdown menu component is missing a trigger, a menu, or a popover content element.', dropdownMenuComponent);
const missing = [];
if (!trigger) missing.push('trigger');
if (!menu) missing.push('menu');
if (!popover) missing.push('popover');
console.error(`Dropdown menu initialisation failed. Missing element(s): ${missing.join(', ')}`, dropdownMenuComponent);
return;
}
@@ -18,7 +22,7 @@
trigger.removeAttribute('aria-activedescendant');
popover.setAttribute('aria-hidden', 'true');
trigger.focus();
activeIndex = -1;
setActiveItem(-1);
};
const openMenu = () => {
@@ -53,17 +57,17 @@
}
});
dropdownMenuComponent.addEventListener('keydown', (e) => {
dropdownMenuComponent.addEventListener('keydown', (event) => {
const isExpanded = trigger.getAttribute('aria-expanded') === 'true';
if (e.key === 'Escape') {
if (event.key === 'Escape') {
if (isExpanded) closeMenu();
return;
}
if (!isExpanded) {
if (['ArrowDown', 'ArrowUp', 'Enter', ' '].includes(e.key)) {
e.preventDefault();
if (['ArrowDown', 'ArrowUp', 'Enter', ' '].includes(event.key)) {
event.preventDefault();
openMenu();
}
return;
@@ -73,26 +77,30 @@
let nextIndex = activeIndex;
switch (e.key) {
switch (event.key) {
case 'ArrowDown':
e.preventDefault();
nextIndex = activeIndex < menuItems.length - 1 ? activeIndex + 1 : 0;
break;
event.preventDefault();
if (activeIndex < menuItems.length - 1) {
nextIndex = activeIndex + 1;
}
break;
case 'ArrowUp':
e.preventDefault();
nextIndex = activeIndex > 0 ? activeIndex - 1 : menuItems.length - 1;
event.preventDefault();
if (activeIndex > 0) {
nextIndex = activeIndex - 1;
}
break;
case 'Home':
e.preventDefault();
event.preventDefault();
nextIndex = 0;
break;
case 'End':
e.preventDefault();
event.preventDefault();
nextIndex = menuItems.length - 1;
break;
case 'Enter':
case ' ':
e.preventDefault();
event.preventDefault();
menuItems[activeIndex]?.click();
closeMenu();
return;
@@ -103,14 +111,14 @@
}
});
menu.addEventListener('click', (e) => {
if (e.target.closest('[role^="menuitem"]')) {
menu.addEventListener('click', (event) => {
if (event.target.closest('[role^="menuitem"]')) {
closeMenu();
}
});
document.addEventListener('click', (e) => {
if (!dropdownMenuComponent.contains(e.target)) {
document.addEventListener('click', (event) => {
if (!dropdownMenuComponent.contains(event.target)) {
closeMenu();
}
});
+8 -5
View File
@@ -4,7 +4,10 @@
const content = popoverComponent.querySelector(':scope > [data-popover]');
if (!trigger || !content) {
console.error('Popover component is missing a trigger button or a content element.', popoverComponent);
const missing = [];
if (!trigger) missing.push('trigger');
if (!content) missing.push('content');
console.error(`Popover initialisation failed. Missing element(s): ${missing.join(', ')}`, popoverComponent);
return;
}
@@ -36,14 +39,14 @@
}
});
popoverComponent.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
popoverComponent.addEventListener('keydown', (event) => {
if (event.key === 'Escape') {
closePopover();
}
});
document.addEventListener('click', (e) => {
if (!popoverComponent.contains(e.target)) {
document.addEventListener('click', (event) => {
if (!popoverComponent.contains(event.target)) {
closePopover();
}
});
+65 -24
View File
@@ -6,12 +6,25 @@
const listbox = popover.querySelector('[role="listbox"]');
const input = selectComponent.querySelector(':scope > input[type="hidden"]');
const filter = selectComponent.querySelector('header input[type="text"]');
if (!trigger || !popover || !listbox || !input) return;
if (!trigger || !popover || !listbox || !input) {
const missing = [];
if (!trigger) missing.push('trigger');
if (!popover) missing.push('popover');
if (!listbox) missing.push('listbox');
if (!input) missing.push('input');
console.error(`Select component initialisation failed. Missing element(s): ${missing.join(', ')}`, selectComponent);
return;
}
const options = Array.from(listbox.querySelectorAll('[role="option"]'));
let visibleOptions = [...options];
let activeIndex = -1;
const hasTransition = () => {
const style = getComputedStyle(popover);
return parseFloat(style.transitionDuration) > 0 || parseFloat(style.transitionDelay) > 0;
};
const updateValue = (option) => {
if (option) {
selectedValue.innerHTML = option.dataset.label || option.innerHTML;
@@ -21,14 +34,26 @@
}
};
const closePopover = () => {
const closePopover = (focusOnTrigger = true) => {
if (popover.getAttribute('aria-hidden') === 'true') return;
if (filter) {
const resetFilter = () => {
filter.value = '';
visibleOptions = [...options];
options.forEach(opt => opt.setAttribute('aria-hidden', 'false'));
};
if (hasTransition()) {
popover.addEventListener('transitionend', resetFilter, { once: true });
} else {
resetFilter();
}
}
if (focusOnTrigger) trigger.focus();
popover.setAttribute('aria-hidden', 'true');
trigger.setAttribute('aria-expanded', 'false');
if (filter) {
filter.value = '';
visibleOptions = [...options];
options.forEach(opt => opt.setAttribute('aria-hidden', 'false'));
}
trigger.removeAttribute('aria-activedescendant');
if (activeIndex > -1) options[activeIndex]?.classList.remove('active');
activeIndex = -1;
@@ -37,10 +62,17 @@
const selectOption = (option) => {
if (!option) return;
if (option.dataset.value) {
if (option.dataset.value != null) {
updateValue(option);
}
closePopover();
const event = new CustomEvent('change', {
detail: { value: option.dataset.value },
bubbles: true
});
selectComponent.dispatchEvent(event);
};
if (filter) {
@@ -67,36 +99,36 @@
filter.addEventListener('input', filterOptions);
}
let initialOption = options.find(opt => input.value && opt.dataset.value === input.value);
let initialOption = options.find(opt => opt.dataset.value === input.value);
if (!initialOption && options.length > 0) initialOption = options[0];
updateValue(initialOption);
const handleKeyNavigation = (e) => {
const handleKeyNavigation = (event) => {
const isPopoverOpen = popover.getAttribute('aria-hidden') === 'false';
if (!['ArrowDown', 'ArrowUp', 'Enter', 'Home', 'End', 'Escape'].includes(e.key)) {
if (!['ArrowDown', 'ArrowUp', 'Enter', 'Home', 'End', 'Escape'].includes(event.key)) {
return;
}
if (!isPopoverOpen) {
if (e.key !== 'Enter' && e.key !== 'Escape') {
e.preventDefault();
if (event.key !== 'Enter' && event.key !== 'Escape') {
event.preventDefault();
trigger.click();
}
return;
}
e.preventDefault();
event.preventDefault();
if (e.key === 'Escape') {
if (event.key === 'Escape') {
closePopover();
return;
}
if (e.key === 'Enter') {
if (event.key === 'Enter') {
if (activeIndex > -1) {
selectOption(visibleOptions[activeIndex]);
selectOption(options[activeIndex]);
}
return;
}
@@ -106,7 +138,7 @@
const currentVisibleIndex = activeIndex > -1 ? visibleOptions.indexOf(options[activeIndex]) : -1;
let nextVisibleIndex = currentVisibleIndex;
switch (e.key) {
switch (event.key) {
case 'ArrowDown':
if (currentVisibleIndex < visibleOptions.length - 1) {
nextVisibleIndex = currentVisibleIndex + 1;
@@ -154,9 +186,18 @@
if (isExpanded) {
closePopover();
} else {
if (filter) {
if (hasTransition()) {
popover.addEventListener('transitionend', () => {
filter.focus();
}, { once: true });
} else {
filter.focus();
}
}
popover.setAttribute('aria-hidden', 'false');
trigger.setAttribute('aria-expanded', 'true');
if (filter) filter.focus();
const selectedOption = listbox.querySelector('[role="option"][aria-selected="true"]');
if (selectedOption) {
@@ -173,16 +214,16 @@
}
});
listbox.addEventListener('click', (e) => {
const clickedOption = e.target.closest('[role="option"]');
listbox.addEventListener('click', (event) => {
const clickedOption = event.target.closest('[role="option"]');
if (clickedOption) {
selectOption(clickedOption);
}
});
document.addEventListener('click', (e) => {
if (!selectComponent.contains(e.target)) {
closePopover();
document.addEventListener('click', (event) => {
if (!selectComponent.contains(event.target)) {
closePopover(false);
}
});
+16 -10
View File
@@ -55,18 +55,24 @@
const sidebarId = sidebarComponent.id;
window.addEventListener('sidebar:open', (e) => {
if (!e.detail?.id || e.detail.id === sidebarId) setState(true);
});
window.addEventListener('sidebar:close', (e) => {
if (!e.detail?.id || e.detail.id === sidebarId) setState(false);
});
window.addEventListener('sidebar:toggle', (e) => {
if (!e.detail?.id || e.detail.id === sidebarId) setState(!open);
document.addEventListener('basecoat:sidebar', (event) => {
if (event.detail?.id && event.detail.id !== sidebarId) return;
switch (event.detail?.action) {
case 'open':
setState(true);
break;
case 'close':
setState(false);
break;
default:
setState(!open);
break;
}
});
sidebarComponent.addEventListener('click', (e) => {
const target = e.target;
sidebarComponent.addEventListener('click', (event) => {
const target = event.target;
const nav = sidebarComponent.querySelector('nav');
const isMobile = window.innerWidth < breakpoint;
+6 -6
View File
@@ -19,19 +19,19 @@
if (activePanel) activePanel.hidden = false;
};
tablist.addEventListener('click', (e) => {
const clickedTab = e.target.closest('[role="tab"]');
tablist.addEventListener('click', (event) => {
const clickedTab = event.target.closest('[role="tab"]');
if (clickedTab) selectTab(clickedTab);
});
tablist.addEventListener('keydown', (e) => {
const currentTab = e.target;
tablist.addEventListener('keydown', (event) => {
const currentTab = event.target;
if (!tabs.includes(currentTab)) return;
let nextTab;
const currentIndex = tabs.indexOf(currentTab);
switch (e.key) {
switch (event.key) {
case 'ArrowRight':
nextTab = tabs[(currentIndex + 1) % tabs.length];
break;
@@ -48,7 +48,7 @@
return;
}
e.preventDefault();
event.preventDefault();
selectTab(nextTab);
nextTab.focus();
});
+8 -8
View File
@@ -15,11 +15,11 @@
toaster.addEventListener('mouseenter', pauseAllTimeouts);
toaster.addEventListener('mouseleave', resumeAllTimeouts);
toaster.addEventListener('click', (e) => {
const actionLink = e.target.closest('.toast footer a');
const actionButton = e.target.closest('.toast footer button');
toaster.addEventListener('click', (event) => {
const actionLink = event.target.closest('.toast footer a');
const actionButton = event.target.closest('.toast footer button');
if (actionLink || actionButton) {
closeToast(e.target.closest('.toast'));
closeToast(event.target.closest('.toast'));
}
});
@@ -109,8 +109,8 @@
try {
const func = new Function('close', actionString);
func(() => closeToast(toast));
} catch (e) {
console.error('Error executing toast action:', e);
} catch (event) {
console.error('Error executing toast action:', event);
}
}
@@ -166,12 +166,12 @@
const initialToaster = document.getElementById('toaster');
if (initialToaster) initToaster(initialToaster);
window.addEventListener('basecoat:toast', (e) => {
window.addEventListener('basecoat:toast', (event) => {
if (!toaster) {
console.error('Cannot create toast: toaster container not found on page.');
return;
}
const config = e.detail?.config || {};
const config = event.detail?.config || {};
const toastElement = createToast(config);
toaster.appendChild(toastElement);
});