mirror of
https://github.com/hunvreus/basecoat.git
synced 2026-11-13 05:38:18 +00:00
Fixing more issues with popovers
This commit is contained in:
@@ -514,12 +514,16 @@
|
||||
[role='menuitemcheckbox'],
|
||||
[role='menuitemradio'] {
|
||||
@apply aria-hidden:hidden [&_svg]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none [&_svg]:shrink-0 [&_svg]:size-4 aria-disabled:opacity-50 aria-disabled:pointer-events-none disabled:opacity-50 disabled:pointer-events-none w-full truncate;
|
||||
@apply focus-visible:bg-accent focus-visible:text-accent-foreground hover:bg-accent hover:text-accent-foreground;
|
||||
|
||||
&:not([aria-disabled='true']) {
|
||||
@apply focus-visible:bg-accent focus-visible:text-accent-foreground hover:bg-accent hover:text-accent-foreground;
|
||||
}
|
||||
|
||||
&.active {
|
||||
@apply bg-accent text-accent-foreground;
|
||||
}
|
||||
}
|
||||
[role='menu'] [role="presentation"] {
|
||||
[role='menu'] [role='heading'] {
|
||||
@apply flex px-2 py-1.5 text-sm font-medium;
|
||||
}
|
||||
[role='separator'] {
|
||||
@@ -734,7 +738,7 @@
|
||||
@apply bg-[image:var(--check-icon)] bg-no-repeat bg-position-[center_right_0.5rem] bg-size-[0.875rem];
|
||||
}
|
||||
}
|
||||
[role='listbox'] [role="presentation"] {
|
||||
[role='listbox'] [role='heading'] {
|
||||
@apply flex text-muted-foreground px-2 py-1.5 text-xs;
|
||||
}
|
||||
[role='listbox'] [role='group']:not(:has([role='option']:not([aria-hidden='true']))) {
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
>
|
||||
<div role="presentation" id="{{ group_label_id }}">{{ item.label }}</div>
|
||||
<div role="heading" id="{{ group_label_id }}">{{ item.label }}</div>
|
||||
{{ render_dropdown_items(item.items, item_id) if item.items }}
|
||||
</div>
|
||||
{% elif item.type == "separator" %}
|
||||
|
||||
@@ -151,7 +151,7 @@
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
>
|
||||
<div role="presentation" id="{{ group_label_id }}">{{ item.label }}</div>
|
||||
<div role="heading" id="{{ group_label_id }}">{{ item.label }}</div>
|
||||
{{ render_select_items(item.items, selected, item_id) if item.items }}
|
||||
</div>
|
||||
{% elif item.type == "separator" %}
|
||||
|
||||
+31
-18
@@ -16,19 +16,30 @@
|
||||
let menuItems = [];
|
||||
let activeIndex = -1;
|
||||
|
||||
const closeMenu = () => {
|
||||
const closePopover = (focusOnTrigger = true) => {
|
||||
if (trigger.getAttribute('aria-expanded') === 'false') return;
|
||||
trigger.setAttribute('aria-expanded', 'false');
|
||||
trigger.removeAttribute('aria-activedescendant');
|
||||
popover.setAttribute('aria-hidden', 'true');
|
||||
trigger.focus();
|
||||
|
||||
if (focusOnTrigger) {
|
||||
trigger.focus();
|
||||
}
|
||||
|
||||
setActiveItem(-1);
|
||||
};
|
||||
|
||||
const openMenu = () => {
|
||||
const openPopover = () => {
|
||||
document.dispatchEvent(new CustomEvent('basecoat:popover', {
|
||||
detail: { source: dropdownMenuComponent }
|
||||
}));
|
||||
|
||||
trigger.setAttribute('aria-expanded', 'true');
|
||||
popover.setAttribute('aria-hidden', 'false');
|
||||
menuItems = Array.from(menu.querySelectorAll('[role^="menuitem"]:not([disabled])'));
|
||||
menuItems = Array.from(menu.querySelectorAll('[role^="menuitem"]')).filter(item =>
|
||||
!item.hasAttribute('disabled') &&
|
||||
item.getAttribute('aria-disabled') !== 'true'
|
||||
);
|
||||
if (menuItems.length > 0) {
|
||||
setActiveItem(0);
|
||||
}
|
||||
@@ -51,9 +62,9 @@
|
||||
trigger.addEventListener('click', () => {
|
||||
const isExpanded = trigger.getAttribute('aria-expanded') === 'true';
|
||||
if (isExpanded) {
|
||||
closeMenu();
|
||||
closePopover();
|
||||
} else {
|
||||
openMenu();
|
||||
openPopover();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -61,14 +72,14 @@
|
||||
const isExpanded = trigger.getAttribute('aria-expanded') === 'true';
|
||||
|
||||
if (event.key === 'Escape') {
|
||||
if (isExpanded) closeMenu();
|
||||
if (isExpanded) closePopover();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isExpanded) {
|
||||
if (['ArrowDown', 'ArrowUp', 'Enter', ' '].includes(event.key)) {
|
||||
event.preventDefault();
|
||||
openMenu();
|
||||
openPopover();
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -80,15 +91,11 @@
|
||||
switch (event.key) {
|
||||
case 'ArrowDown':
|
||||
event.preventDefault();
|
||||
if (activeIndex < menuItems.length - 1) {
|
||||
nextIndex = activeIndex + 1;
|
||||
}
|
||||
break;
|
||||
nextIndex = Math.min(activeIndex + 1, menuItems.length - 1);
|
||||
break;
|
||||
case 'ArrowUp':
|
||||
event.preventDefault();
|
||||
if (activeIndex > 0) {
|
||||
nextIndex = activeIndex - 1;
|
||||
}
|
||||
nextIndex = Math.max(activeIndex - 1, 0);
|
||||
break;
|
||||
case 'Home':
|
||||
event.preventDefault();
|
||||
@@ -102,7 +109,7 @@
|
||||
case ' ':
|
||||
event.preventDefault();
|
||||
menuItems[activeIndex]?.click();
|
||||
closeMenu();
|
||||
closePopover();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -113,13 +120,19 @@
|
||||
|
||||
menu.addEventListener('click', (event) => {
|
||||
if (event.target.closest('[role^="menuitem"]')) {
|
||||
closeMenu();
|
||||
closePopover();
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('click', (event) => {
|
||||
if (!dropdownMenuComponent.contains(event.target)) {
|
||||
closeMenu();
|
||||
closePopover();
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('basecoat:popover', (event) => {
|
||||
if (event.detail.source !== dropdownMenuComponent) {
|
||||
closePopover(false);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
+14
-2
@@ -11,14 +11,20 @@
|
||||
return;
|
||||
}
|
||||
|
||||
const closePopover = () => {
|
||||
const closePopover = (focusOnTrigger = true) => {
|
||||
if (trigger.getAttribute('aria-expanded') === 'false') return;
|
||||
trigger.setAttribute('aria-expanded', 'false');
|
||||
content.setAttribute('aria-hidden', 'true');
|
||||
trigger.focus();
|
||||
if (focusOnTrigger) {
|
||||
trigger.focus();
|
||||
}
|
||||
};
|
||||
|
||||
const openPopover = () => {
|
||||
document.dispatchEvent(new CustomEvent('basecoat:popover', {
|
||||
detail: { source: popoverComponent }
|
||||
}));
|
||||
|
||||
const elementToFocus = content.querySelector('[autofocus]');
|
||||
if (elementToFocus) {
|
||||
content.addEventListener('transitionend', () => {
|
||||
@@ -51,6 +57,12 @@
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('basecoat:popover', (event) => {
|
||||
if (event.detail.source !== popoverComponent) {
|
||||
closePopover(false);
|
||||
}
|
||||
});
|
||||
|
||||
popoverComponent.dataset.popoverInitialized = true;
|
||||
};
|
||||
|
||||
|
||||
+40
-27
@@ -148,7 +148,7 @@
|
||||
if (currentVisibleIndex > 0) {
|
||||
nextVisibleIndex = currentVisibleIndex - 1;
|
||||
} else if (currentVisibleIndex === -1) {
|
||||
nextVisibleIndex = 0; // Start from top if nothing is active
|
||||
nextVisibleIndex = 0;
|
||||
}
|
||||
break;
|
||||
case 'Home':
|
||||
@@ -180,37 +180,44 @@
|
||||
filter.addEventListener('keydown', handleKeyNavigation);
|
||||
}
|
||||
|
||||
const openPopover = () => {
|
||||
document.dispatchEvent(new CustomEvent('basecoat:popover', {
|
||||
detail: { source: selectComponent }
|
||||
}));
|
||||
|
||||
if (filter) {
|
||||
if (hasTransition()) {
|
||||
popover.addEventListener('transitionend', () => {
|
||||
filter.focus();
|
||||
}, { once: true });
|
||||
} else {
|
||||
filter.focus();
|
||||
}
|
||||
}
|
||||
|
||||
popover.setAttribute('aria-hidden', 'false');
|
||||
trigger.setAttribute('aria-expanded', 'true');
|
||||
|
||||
const selectedOption = listbox.querySelector('[role="option"][aria-selected="true"]');
|
||||
if (selectedOption) {
|
||||
if (activeIndex > -1) {
|
||||
options[activeIndex]?.classList.remove('active');
|
||||
}
|
||||
activeIndex = options.indexOf(selectedOption);
|
||||
selectedOption.classList.add('active');
|
||||
if (selectedOption.id) {
|
||||
trigger.setAttribute('aria-activedescendant', selectedOption.id);
|
||||
}
|
||||
selectedOption.scrollIntoView({ block: 'nearest' });
|
||||
}
|
||||
};
|
||||
|
||||
trigger.addEventListener('click', () => {
|
||||
const isExpanded = trigger.getAttribute('aria-expanded') === 'true';
|
||||
|
||||
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');
|
||||
|
||||
const selectedOption = listbox.querySelector('[role="option"][aria-selected="true"]');
|
||||
if (selectedOption) {
|
||||
if (activeIndex > -1) {
|
||||
options[activeIndex]?.classList.remove('active');
|
||||
}
|
||||
activeIndex = options.indexOf(selectedOption);
|
||||
selectedOption.classList.add('active');
|
||||
if (selectedOption.id) {
|
||||
trigger.setAttribute('aria-activedescendant', selectedOption.id);
|
||||
}
|
||||
selectedOption.scrollIntoView({ block: 'nearest' });
|
||||
}
|
||||
openPopover();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -227,6 +234,12 @@
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('basecoat:popover', (event) => {
|
||||
if (event.detail.source !== selectComponent) {
|
||||
closePopover(false);
|
||||
}
|
||||
});
|
||||
|
||||
popover.setAttribute('aria-hidden', 'true');
|
||||
selectComponent.dataset.selectInitialized = true;
|
||||
};
|
||||
|
||||
+1
-1
@@ -166,7 +166,7 @@
|
||||
const initialToaster = document.getElementById('toaster');
|
||||
if (initialToaster) initToaster(initialToaster);
|
||||
|
||||
window.addEventListener('basecoat:toast', (event) => {
|
||||
document.addEventListener('basecoat:toast', (event) => {
|
||||
if (!toaster) {
|
||||
console.error('Cannot create toast: toaster container not found on page.');
|
||||
return;
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
>
|
||||
<div role="presentation" id="{{ group_label_id }}">{{ item.label }}</div>
|
||||
<div role="heading" id="{{ group_label_id }}">{{ item.label }}</div>
|
||||
{{ render_dropdown_items(item.items, item_id) if item.items }}
|
||||
</div>
|
||||
{% elif item.type == "separator" %}
|
||||
|
||||
@@ -151,7 +151,7 @@
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
>
|
||||
<div role="presentation" id="{{ group_label_id }}">{{ item.label }}</div>
|
||||
<div role="heading" id="{{ group_label_id }}">{{ item.label }}</div>
|
||||
{{ render_select_items(item.items, selected, item_id) if item.items }}
|
||||
</div>
|
||||
{% elif item.type == "separator" %}
|
||||
|
||||
Reference in New Issue
Block a user